MySQL InnoDB存储引擎使用MVCC机制来提供一致性非锁定读((consistent nonlocking read)。
为方便演示,下面图中:
黄色部分表示重做日志(UNDO LOG)
绿色部分表示正常数据
红色部分表示已删除数据
假设现在有表TB001,其表结构为:
CREATE TABLE `TB001` ( `ID` VARCHAR(20) NOT NULL, `C1` VARCHAR(20) NOT NULL, `C2` VARCHAR(20) NOT NULL, PRIMARY KEY (`ID`), KEY `IDX_C1` (`C1`) ) ENGINE=INNODB DEFAULT CHARSET=utf8;
步骤1:事务TX1001在12:00时插入一条记录
INSERT INTO TB001(ID,C1,C2) VALUES('AA0001','BB0001','CC0001');
在主键索引/二级索引/重做日志中数据如下:
步骤2:事务TX1003更新数据但未提交:
UPDATE TB001 SET C2='CC1111' WHERE ID='AA0001'
步骤3:事务TX99999999999开启可重复读事务,开启事务执行第一条SQL语句时:
A、当前所有TX1001及之前事务都已提交,则事务低水位线为TX1001
B、当前写事务最大ID为TX1004,则事务高水位线TX1005
C、事务TX1002和TX1004都已提交
D、事务TX1003未提交,则活跃事务集为(TX1003)
步骤4:事务TX1003提交更新,更新后在主键索引/二级索引/重做日志中数据如下:
步骤5:事务TX1005执行更新并提交:
UPDATE TB001
SET C1='BB1111' WHERE ID='AA0001'
更新后在主键索引/二级索引/重做日志中数据如下:
步骤6:事务TX1007执行删除并提交:
DELETE FROM TB0001 WHERE ID='AA0001'
更新后在主键索引/二级索引/重做日志中数据如下:
步骤7:事务TX99999999999执行查询:
SELECT * FROM TB001 WHERE C1='BB0001'
查询如何执行呢?
1、首先当前二级索引IDX_C1上有两条已标记为删除的记录(C1=BB0001,ID=AA001)和(C1=BB1111,ID=AA0001)
2、通过在索引IDX_C1按C1='BB0001'查找可以找到已标记为删除的记录(C1=BB0001,ID=AA001)
3、根据索引记录的ID=AA0001在主键索引上找到已删除记录(ID=AA0001,C1=BB1111,C2=CC1111,TRX=TX1007,RBP=RP1007)
4、步骤3中找到的记录版本为TX1007,超过事务TX99999999999的高水位线TX1005,版本不满足,按照回滚指针RP1007去UNDO日志中查找
5、在UNDO日志中找到(TX1005,RPT=RP1005,ID=AA001,C1=BB1111,C2=CC11111),和步骤3中记录合并得(TX1005,RPT=RP1005,ID=AA001,C1=BB1111,C2=CC11111)
6、步骤5找到记录版本为TX1005,等于事务TX99999999999的高水位线,版本不满足,按照回滚指针RP1005去UNDO日志中查找上一版本
7、在UNDO日志中找到(TX1003,RPT=RP1003,ID=AA001,C1=BB0001),和步骤5中记录合并得(TX1003,RPT=RP1003,ID=AA001,C1=BB0001,C2=CC11111)
8、步骤5找到记录版本为TX1003,虽然高于"低水位线"和低于“高水位线”,但在活跃事务集(TX1003)中,版本不满足,按照回滚指针RP1003去UNDO日志中查找上一版本
9、在UNDO日志中找到(TX1001,RPT=RP1001,ID=AA001,C2=CC001),与步骤7中记录合并得(TX1001,RPT=RP1001,C1=BB0001,ID=AA001,C2=CC001)
10、步骤9找到的记录版本为TX1001等于“低水位线”,版本满足,将记录(TX1001,RPT=RP1001,C1=BB0001,ID=AA001,C2=CC001)返回给查询客户端。
11、查询结束。