众所周知MySQL中InnoDB的可重复读是通过MVCC实现的。
MVCC是由哪些元素构成?
MVCC的读 又叫快照读,在MySQL中不可能把整个表的数据拷贝一份来提供这个快照。它通过
1、undolog
InnoDB增删数据时除了redo log外还会记录undo log。
以update举例: undolog->修改内存中数据页->redolog prepare->binlog-> commit; 修改数据时 undo log记录原始数据。用于以后或许会发生的回滚操作。
2、表中的的隐藏列 data_trx_id,data_roll_pt
一张表中有三个隐藏列
rows_id: MySQL给予行的一个标识。如果表中没有主键,那么就会使用row_id来组织数据。
data_trx_id: 事务id,创建一个新的事务,会分配一个自增的全局唯一的data_trx_id。
需要说明的是:begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个操作 InnoDB 表的语句,事务才真正启动。
data_roll_pt: 回滚指针,这个指针指向undo log中的一行记录原始记录。
3、Read View数组
创建事务时,InnoDB为事务创建了一个Read View的数组用来记录创建事务时刻的当前活跃事务data_trx_id。
低水位:创建当前事务时刻活跃事务中最小的data_trx_id。很多人认为是已提交事务的最大data_trx_id,这样理解之后存在一个问题。
比如 现在已提交事务有 1 3 ,2为活跃事务。按已提交事务的最大id来理解,低水位应该为3。与MySQL判断 小于低水位为已提交事务,就出现了矛盾。
高水位:创建当前事务时刻未分配的最小data_trx_id。
以上三部分来实现。
MVCC怎么工作的?
当读取到一行数据
0、是否是当前事务修改,是当前事务修改,当前事务可见。
1、这行数据data_trx_id与低水位比较,小于低水位,则事务已提交,当前事务可见。否则判断是否大于高水位。
2、这行数据data_trx_id与高水位比较,大于高水位,则是在当前事务创建之后的事务,当前事务不可见。
3、这行数据data_trx_id与高水位比较,小于高水位,则判断Read View中是否包含此data_trx_id。如果包含则表示
当前事务创建时,data_trx_id为活跃事务,当前事务不可见。
4、Read View中不含此data_trx_id则表示当前事务创建时,data_trx_id为已提交事务,当前事务可见。
举个栗子:现在已提交事务有 1 3 ,2为活跃事务,当前事务data_trx_id为4。低水位为2,高水位5。
此时Read View中为 2 4。当读到data_trx_id为3的行时,
1、3<2?
2、3>5?
3、Read View包含3?
最后得出3对4可见。
跟undolog有什么关系?
沿用上面的例子, 新起一个事务update对原来为data_trx_id为3的已提交事务进行操作。记录undolog,将data_trx_id由3改为5,并且data_roll_pt指向undolog中为data_trx_id为3的记录。
当data_trx_id为4的事务,读取到data_trx_id为5的行时,data_trx_id不满足条件则通过data_roll_pt查找undolog中是否有满足条件的行,可以查找到data_trx_id为3的行。