MySQL 中的事务
事务
特性
ACID:原子性、一致性、隔离性、持久性
事务隔离解决的问题
脏读
A 事务执行过程中读取到了 B 事务中未提交的数据
不可重复读
由于在 A 事务两次查询的间隔 B 事务进行了提交,导致 A 事务执行过程中第一次查询的数据和第二次查询的数据不一致
幻读
A 事务第一次查询 id 为 1 的记录时为空,然后 B 事务插入了一条 id 为 1 的数据并提交,之后 A 事务也插入了一条 id 为 1 的记录时会发生错误,错误提示该 id 值已存在,但是当 A 再次查询 id 为 1 的记录是仍然为空,此时A:???
幻读的本质就是事务中读取到的数据和实际的数据不一致,我们把这种一个事务中的写,改变了另外一个事务查询条件的情况称之为 Phantom,这个问题会导致 write skew 问题的发生
实现机制
事务日志
见 Redolog 和 Undolog
事务隔离级别
- 读未提交:不会使用任何的隔离措施,因此它存在脏读、不可重复读、幻读的问题
- 读已提交:通过 MVCC 机制的 ReadView 解决了读未提交中的脏读的问题
- 可重复读:通过 MVCC 机制的 ReadView 解决了读未提交中的脏读和不可重复读的问题
- 串行化:通过锁机制解决了读未提交中的脏读、不可重复读和幻读的问题
锁
锁类型:共享锁(Read)、排他锁(Write)
表锁
LOCK TABLES $table_name READ;
LOCK TABLES $table_name WRITE;
UNLOCK TABLES;
行锁
SELECT * FROM $table_name WHERE condition LOCK IN SHARE MODE;
SELECT * FROM $table_name WHERE condition FOR UPDATE;
日志
Binlog
Binlog 是数据库层提供的功能, 用于记录数据库的写操作(增删改)信息
格式
Binlog 有三种不同的格式,分别为 statement(属于逻辑日志)、row(属于物理日志) 和 mixed。其中 statement 格式记录的是 SQL 语句,因此当执行非幂等 SQL 时无法保证数据的一致性,而 row 格式解决了这个问题,row 格式只记录具体的数据变动,因此它可以解决 statement 存在的问题,但是 row 格式产生的日志量会远大于 statement,因为一条 SQL 语句可能影响成百上千条数据,因此 MySQL 在 5.1.8 中推出了 mixed 格式,在 mixed 格式下默认使用 statement 格式记录日志,当遇到非幂等 SQL 时使用 row 格式记录日志。
作用
- 主从服务
- 数据恢复
Redolog
同 binlog 一样 redolog(属于物理日志) 也是用于记录数据写操作的日志, 不同的是 redolog 是 innodb 存储引擎层提供的功能,它的作用是通过崩溃恢复来确保事务的持久化
背景
由于 MySQL 中事务产生的数据修改是先发生在内存中,然后再被持久化到磁盘的,因此当事务提交且修改未写入磁盘前一旦发生宕机情况就会导致无法保证事务的持久性。所以 innodb 中在内存修改行为发生后同样在内存中使用 redolog 来记录数据变更日志,并且当事务提交时先将内存缓存中的 redolog 持久化到磁盘,然后才返回事务提交成功的确认, 从而避免上述情况。
作用
当服务宕机后重启时数据库会从 redolog 中执行未被持久化的数据变更即可恢复未被持久化的事务操作。
其实 redolog 和事务都需要通过写入磁盘来保证持久性, 但是 redolog 相较于数据库的刷盘是更加轻量的,因此 redolog 可以减轻磁盘操作。而且通过 redolog 可以实现事务持久化的异步提交,可以使数据库快速处理下一个事务,从而提升数据库的并发性能。
Undolog
Undolog(属于逻辑日志) 和 redolog 一样都是 innodb 存储引擎层提供的功能,但是不同的是 undolog 记录的是每个 SQL 语句的反向操作,可以通过 undolog 来进行 rollback 操作从而保证事务的原子性,同时 undolog 也是 MVCC 中的重要组成部分,在 MVCC 中如果获取到的最新数据不满足可见条件就会通过 DATA_ROLL_PTR 来读取 undolog 中的记录
作用
- 回滚事务
- 实现 MVCC