面试题答案
一键面试版本链的形成
- 数据行结构:在MySQL InnoDB存储引擎中,每行数据除了实际存储的数据外,还包含隐藏列。其中有两个关键的隐藏列,分别是
trx_id
和roll_pointer
。trx_id
:每次对某条聚簇索引记录进行修改时,都会把修改该记录的事务ID赋值给trx_id
隐藏列。roll_pointer
:指向这条记录的上一个版本(即undo日志)。通过roll_pointer
,可以将同一数据行的不同版本连接起来,形成版本链。
- undo日志:当对数据进行修改时,InnoDB会先将旧版本的数据写入undo日志。例如,若要更新一行数据,会先把更新前的旧数据写入undo日志,然后再进行实际的更新操作。
roll_pointer
就指向这个undo日志中的旧版本数据,而这个旧版本数据也有自己的trx_id
和roll_pointer
,从而形成一个链表结构,这就是版本链。
版本链在一致性读操作中的角色
- 提供多版本数据:一致性读操作(通常指普通的SELECT语句,不使用
FOR UPDATE
等锁定语句)不会获取锁,而是通过版本链来获取符合事务一致性要求的数据版本。版本链为一致性读提供了在不同时间点的数据版本,使得事务可以看到一个一致性的视图,就好像整个数据库在某个时间点是冻结的一样。 - 保证事务隔离性:通过版本链,InnoDB可以在不同的事务隔离级别下,为每个事务构建一个符合该隔离级别的数据视图。例如在可重复读(RR)隔离级别下,事务在启动时会记录当前系统中活跃的事务ID列表,之后在一致性读时,通过版本链上的
trx_id
与该列表进行比较,来决定使用哪个版本的数据,从而保证事务在整个执行过程中看到的数据是一致的,满足隔离性要求。
一致性读利用版本链获取符合事务一致性要求的数据版本的过程
- 确定事务视图:
- 在可重复读隔离级别下,事务启动时会创建一个事务视图。这个视图包含当前系统中活跃的事务ID列表(这些事务的
trx_id
大于当前事务的trx_id
)。 - 在读已提交(RC)隔离级别下,每次执行一致性读时都会创建一个新的事务视图,同样包含当前活跃的事务ID列表。
- 在可重复读隔离级别下,事务启动时会创建一个事务视图。这个视图包含当前系统中活跃的事务ID列表(这些事务的
- 遍历版本链:
- 当执行一致性读时,从数据行的当前版本开始。
- 检查当前版本的
trx_id
:- 如果当前版本的
trx_id
在事务视图的活跃事务ID列表之外(即trx_id
小于当前事务视图中的最小活跃trx_id
),说明该版本是在当前事务启动之前就已经提交的事务所修改的,这个版本对当前事务可见,可以使用该版本的数据。 - 如果当前版本的
trx_id
在事务视图的活跃事务ID列表之中,说明该版本是由当前事务启动后还未提交的事务所修改的,这个版本对当前事务不可见,需要顺着roll_pointer
找到上一个版本,继续检查上一个版本的trx_id
,重复上述判断过程,直到找到一个对当前事务可见的版本,或者遍历完整个版本链都没有找到合适的版本(此时说明该数据在当前事务启动前不存在)。
- 如果当前版本的