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表隐藏字段,记录当前数据对应的事务IDdb_roll_ptr
InnoDB表隐藏字段,指向 undo log 中的回滚段current read
当前读,读取当前值(最新版本),以保证事务的一致性
read-view
consistent read view
一致性读视图,又称read-view,由3部分组成
rw_trx_ids
一个数组,存储了启动但未提交的事务IDlow_limit_id
rw_trx_ids 中的最小值up_limit_id
rw_trx_ids 中的最大值+1
consistent read
一致性读,又称快照读,适用于普通 SELECT,现在事务中 SELECT 一行记录:
- 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 创建后提交,不可见 - 如果不可见,通过
db_roll_ptr
找出可见版本、计算历史数据
各隔离级别下的 MVCC 工作原理都一样,区别在于生成 read-view 的时机不同:RC 在 SELECT 瞬间生成,RR 在事务启动瞬间生成。
current read
当前读,直接访问位于主键索引的当前值,因此要利用基于锁的并发控制:
UPDATE 更新当前值,加X锁。
SELECT FOR UPDATE 访问当前值,加X锁。
SELECT LOCK IN SHARE MODE 访问当前值,加S锁。