以下基于INNODB引擎,RR隔离级别
MVCC
多版本的并发控制协议
- 隐藏列:InnoDB中每行数据都有隐藏列,隐藏列包含了:本行数据的事务id、指向undo log的指针等。
- 基于undo log的版本链:每行数据的隐藏列中包含了指向undo log的指针,而每条undo log也会指向更早版本的undo log,形成版本链。
- ReadView:通过隐藏列和版本链,Mysql可以将数据恢复到指定版本。具体恢复到哪个由ReadView决定。
所谓ReadView是指事务(A)在某一时刻给整个事务打快照,之后再进行读操作时,会读到事务id和快照比较,从而判断数据对事务A是否可见。
当前读和快照读
- 当前读
像select lock in share mode(共享锁),for update(排他锁)这些操作都是一种当前读。
读取到得是最新版本,还要保证其他并发事务不能修改当前记录,会对记录加锁。
- 快照读
不加锁的select操作;前提是隔离级别不是串行级别,快照读基于MVCC
普通select语句
- RU,不加锁直接读取最新版本,可能出现脏读、不可重复读、幻读
- RC,不加锁每次执行select都会生成一个readView,避免脏读,会出现不可重复读和幻读
- RR,不加锁,只有第一次执行select生成一个readView,避免了脏读、不可重复读和幻读
- 序列化
- 系统变量autocommit=0时,普通select被转换为select...lock in share mode。也就是S锁;
- 系统变脸autocommit=1时,普通select不会加锁,只是利用mvcc生成一个readView,因为启动自动提交意味着一个事务只包含一条语句,只执行一条语句不会出现不可重复读和幻读的现象
当一个事务(creator_trx_id)对数据进行快照读时:
1、生成当前readview
2、拿到数据的最新事务id:trx_id
3、trx_id = creator_trx_id(当前事务id),说明这条数据是当前事务更新的,可以获取
4、trx_id < min_trx_id,数据在当前所有正在运行事务前更新,可以获取
5、trx_id >= max_trx_id,数据被生成readview之后的事务更改,不能访问
6、生成readview时正在运行的事务列表包含trx_id,数据是被当前列表的事务更新,通过undolog找到记录的上一个事务id,回到步骤2
7、trx_list不包含trx_id,可以访问(假设当前事务id = 10,比如id = 14 和 id = 15事务,在生成readview前id = 14 事务提交,导致一系列条件不符合,也不在trx_id中,建立readview后读取的是最新id = 14的记录)
8、结束