Atomicity 原子性,保证一组操作,要么全部成功,要么全部失败。
Consistency 一致性,执行事务前后,数据保持一致。
Isolation 隔离性,一个用户的事务不被其他事务所干扰。
Durability 持久性,事务提交后对数据库的修改是持久的。

隔离级别

事务在并发过程中可能遇到以下不一致现象:

  • 脏读 T1 修改数据但未提交,T2 访问了这个脏数据。
  • 不可重复读 T1 读取部分数据,随后 T2 修改了这部分数据,当 T1 重复读同样的数据时,发现已经不一样了。
  • 幻读 T1 读取了部分数据,随后 T2 又插入了几行数据,那么 T1 在随后的查询中,会发现多了几行原本不存在的记录。不可重复读的重点是修改,幻读的重点是新增、删除。

事务的隔离级别是由消除以上哪些现象所定义的:

Isolation level Dirty reads Nonrepeatable reads Phantoms
Read uncommitted X X X
Read committed X X
Repeatable read X
Serializable
  • Read Uncommited 可读取别的事务修改但未提交的数据。
  • Read Commited 只读取别的事务提交后的数据。
  • Reaptable Read 只读取开启事务前,已经提交的数据。InnoDB 的 RR 通过 MVCC 解决了幻读。
  • Serializable 冲突事务串行执行,不允许脏读、不可重复读、幻读。

InnoDB 默认隔离级别 RR,是因为 MySQL5.0 前的 binlog 只有 statement 模式,该模式下的事务提交顺序和 binlog 记录的顺序不一致,将导致主从不一致。
我们生产环境使用 MySQL 5.7.28,开启 binlog 的 row 模式,使用 RC 隔离级别 set tx_isolation='read-committed;

MVCC 实现事务

多版本并发控制(Multi-version Concurrency Control),不同于基于锁的并发控制,它让读写操作互不阻塞,每一个写操作都会创建一个新版本的数据,读操作会从有限多个版本的数据中挑选一个最合适的结果直接返回,由此解决了事务的竞争条件。
transaction id 事务ID,唯一自增
db_trx_id InnoDB表隐藏字段,记录当前数据对应的事务ID
db_roll_ptr InnoDB表隐藏字段,指向 undo log 中的回滚段current read 当前读,读取当前值(最新版本),以保证事务的一致性

read-view

consistent read view 一致性读视图,又称read-view,由3部分组成

rw_trx_ids 一个数组,存储了启动但未提交的事务ID low_limit_id rw_trx_ids 中的最小值 up_limit_id rw_trx_ids 中的最大值+1

consistent read

一致性读,又称快照读,适用于普通 SELECT,现在事务中 SELECT 一行记录:

  1. InnoDB 判断该行记录的 db_trx_id
    1.1 等于 transaction id,版本在事务内部生成,可见
    1.2 小于 up_limit_id,版本在 read-view 创建前提交,可见
    1.3 大于 low_limit_id,版本在 raed-view 创建后提交,不可见
    1.4 存在 rw_trx_ids,版本在 read-view 创建后提交,不可见
  2. 如果不可见,通过 db_roll_ptr 找出可见版本、计算历史数据

各隔离级别下的 MVCC 工作原理都一样,区别在于生成 read-view 的时机不同:RC 在 SELECT 瞬间生成,RR 在事务启动瞬间生成。

current read

当前读,直接访问位于主键索引的当前值,因此要利用基于锁的并发控制:
UPDATE 更新当前值,加X锁。
SELECT FOR UPDATE 访问当前值,加X锁。
SELECT LOCK IN SHARE MODE 访问当前值,加S锁。

参考文献

  1. 一文带你轻松搞懂事务隔离级别
  2. 互联网项目中mysql应该选什么事务隔离级别
  3. Transaction Isolation Levels
  4. MySQL实战45讲:事务到底是隔离的还是不隔离的
  5. MySQL 一致性非锁定读