文章意在抛砖引玉,因本人能力有限,如有不对的地方,还请指正
基础概念
- 每个事物启动时,会像innodb的事物系统申请一个 transaction id(后续简称TXID),此ID全局唯一,按申请顺序递增。
- DB_TRX_ID, 6byte, 创建这条记录/最后一次更新这条记录的事务ID
- DB_ROLL_PTR, 7byte,回滚指针,指向这条记录的上一个版本(存储于rollback segment里,undo log内)
- DB_ROW_ID, 6byte,隐含的自增ID,如果数据表没有主键,InnoDB会自动以DB_ROW_ID维护一个聚簇索引
- 记录头中还会有一个deleted flag:意味着该记录会被purge operation 清除
具体应用
repeatable read隔离级别
每个事物启动时(begin为第一条select语句执行时,start transaction with consistent snapshot为立即构建)内部会构造一个read-view的结构,read-view里维护了当前事务启动时系统内活跃TXID的集合,这些活跃TXID的最小值为up_limit_id(低水位),最大值为low_limit_id(高水位)。
数据的可见性定义
- DB_TRX_ID<up_limit_id:表明数据在当前事务启动前已更新完成,可见
- DB_TRX_ID>low_limit_id:表明更新这条记录的事务在当前事务启动之后才启动,不可见
- DB_TRX_ID=TXID:自己更新的,可见
- up_limit_id<DB_TRX_ID<low_limit_id
- DB_TRX_ID ∈ read_view:说明更新这条记录的事务在当前事务启动的时候还未提交,不可见
- DB_TRX_ID ? read_view:说明更新这条记录的事务在当前事务启动的时候已经提交,可见
最后返回deleted flag=0的记录
read committed 隔离级别
这个read-view会在每次select时生成(这种情况感觉不生成性能还高一些),所以,其每次查询,都能看到其他事务已提交的数据
扩展
在INNODB二级索引中,是不存在上述两个隐藏列的 ,但是索引页会有一个MAX_TRX_ID变量来记录最后一次更新二级索引页的TXID,当然,deleted flag也是有的。
所以这里会有一些情况导致查询时必须回表去判断数据可见性,导致覆盖索引失效
数据的可见性定义
- MAX_TRX_ID<up_limit_id:数据可见,覆盖索引有效,返回所有deleted flag =0的记录
- 否则,需回表判断数据可见性
总结
- 通过主键查找记录,记录的可见性依赖于read_view,DATA_TRX_ID(当前行的最新版本),DATA_ROLL_PTR(回滚指针,指向上一个版本)
- RR级别,read_view生成于第一条select语句。RC级别,每次select都会生成read_view
- 通过二级索引查找记录,部分情况下可见性需要依赖聚集索引