近期看到这样一个问题:走覆盖索引不用回表,但是如何用MVCC判断可见性?因为二级索引上没有隐藏列trx_id和roll_ptr。而MVCC是基于trx_id和roll_ptr。那走覆盖索引如何判断是否可见呢?
前置知识
参考资料
https://www.modb.pro/db/173167
https://www.zhihu.com/question/27674363/answer/38034982
《Innodb存储引擎》
以下讨论基于Innodb的RR隔离级别
聚簇索引行记录
根据《Innodb存储引擎》:InnoDB每行有隐藏列TransactionID和Roll Pointer
其中TransactionID是用于记录修改该记录的最新事务id,用于MVCC判断可见性及回滚。
而Roll Pointer也就是回滚指针,用于配合undolog进行数据版本回溯。
二级索引记录
问题中提到二级索引上没有隐藏列trx_id和roll_ptr,这的确是正确的。
但二级索引的页面具有PAGE_MAX_TRX_ID,用于记录修改页内数据的最新事务id。
所以
如果PAGE_MAX_TRX_ID的值是在Read View创建前提交的,那么该页的全部索引都可见;
如果不是,则需要根据二级索引的主键进行回表,进行再次判断。
总结
二级索引的页面含有PAGE_MAX_TRX_ID用于记录修改页内数据的最新事务id,如果PAGE_MAX_TRX_ID的值是在Read View创建前提交的,那么该页的全部索引都可见;如果不是,则需要根据二级索引的主键进行回表,进行再次判断。
所以覆盖索引不是总不需要回表的。
引申思考
为什么二级索引要选择存储页面最新的事务id,而非单条索引记录的最新事务id?
如果每条索引记录都存储最新事务id,那么可以减少不必要的回表,因为页内可能有一部分索引记录的最新事务是在当前事务创建之前提交的。
而以页面为单位存储最新事务id可以减少索引记录的数据量。
个人认为这是一种效率与空间之间的权衡。