MVCC就是为了解决读写冲突
多版本:MySQL维护一条记录的多个版本
并发控制:在多个事务同时操作同一条数据时,决策应该返回的数据是哪一个版本
两种读
一致性非阻塞快照读:
不加锁的 SELECT 语句就是快照读,读取的是记录数据的可见版本,可能是历史数据
-
RC级别:每次select快照读都会重新生成一个Read View,所以能看到本次快照读之前其他已提交事务的数据版本
-
RR级别:事务开始后的第一个select快照读生成当前事务的Read View,整个事务期间都复用这个一致性视图
悲观锁阻塞当前读:
增删改时读取当前行数据 在版本链中最新版本的视图,读取时要保证其他并发事务不能修改当前记录,会对读取的记录添加行锁
- SELECT ... FOR UPDATE / LOCK IN SHARE MODE、INSERT、UPDATE、DELETE都是当前读
-
Next-Key Locks(临键锁):在RR隔离级别下,为了防止幻读,当前读不仅对特定行加锁,还会对索引间隙加锁。
Read View
四个属性
m_ids | 活跃中的事务id的列表 | 集合 |
min_trx_id | 活跃中的最小事务id | m_ids中最小值 |
max_trx_id | 预分配给下一个事务的id | 当前最大事务id + 1 |
creator_trx_id | 创建 ReadView 时所在的事务id | 当前事务id |
版本可见性判断条件
将生成版本链节点的trx_id与当前的Read View进行比对
- trx_id = creator_trx_id ,说明是当前事务生成的版本,可见
- trx_id < min_trx_id , 说明该版本为已提交事务生成,可见
- trx_id > max_trx_id , 说明是在当前事务之后开启的事务,不可见
- min_trx_id <= trx_id <= max_trx_id ,
- 在m_ids中,说明生成该版本数据的事务仍旧活跃,未提交,不可见
- 不在m_ids中,可见
总结就是:自己的可见,已提交的可见
Undo Log 版本链示意图
Undo log
使得不同事务能够看到特定时间点的数据快照,从而避免了读写冲突。
存储旧版本数据,用于回滚操作和构建一致性读的版本链。
-
db_trx_id:创建当前视图的事务的id
-
db_roll_ptr:回滚指针,指向
undo log
中的旧版本数据
没有主键和唯一索引时,会有一个隐式字段 row_id 代替主键的作用对表进行排序