所谓一致性非锁定读就是每行记录可能存在多个历史版本,多版本之间串联起来形成了一条版本链,这样不同时刻启动的事务可以无锁地访问到不同版本的数据。
undo log 版本链
一致性非锁定读是通过 MVCC(Multi Version Concurrency Control,多版本并发控制) 来实现的。事实上,MVCC 没有一个统一的实现标准,所以各个存储引擎的实现机制不尽相同。
InnoDB 存储引擎中 MVCC 的实现是通过 undo log 来完成的,undo log 是啥?
简单理解,undo log 就是每次操作的反向操作,比如比如当前事务执行了一个插入 id = 100 的记录的操作,那么 undo log 中存储的就是删除 id = 100 的记录的操作。
所以,这里用多版本来形容并不是非常准确,因为 InnoDB 并不会真正地去开辟空间存储多个版本的行记录,只是借助 undo log 记录每次写操作的反向操作。
也就是说,B+ 索引树上对应的记录只会有一个最新版本,只不过 InnoDB 可以根据 undo log 得到数据的历史版本,从而实现多版本控制。
那么,还有个问题,undo log 是如何和某条行记录产生联系的呢?换句话说,我怎么能通过这条行记录找到它拥有的 undo log 呢?
具体来说,InnoDB 存储引擎中每条行记录其实都拥有两个隐藏的字段:trx_id
和 roll_pointer
。
从名字也能看出来,trx_id
就是最近更新这条行记录的事务 ID,roll_pointer
就是指向之前生成的 undo log。
掏出我们的 user 表,来举个例子,假设 id = 100 的事务 A 插入一条行记录(id = 1, username = "Jack", age = 18),那么,这行记录的两个隐藏字段 trx_id = 100
和 roll_pointer
指向一个空的 undo log,因为在这之前并没有事务操作 id = 1 的这行记录。如图所示:
然后,id = 200 的事务 B 修改了这条行记录,把 age 从 18 修改成了 20,于是,这条行记录的 trx_id
就变成了 200,rooll_pointer
就指向事务 A 生成的