文章目录

  • 前言
  • 1. 隐藏字段
  • 2. undo log 执行流程
  • 3. undo log 与 read view(快照)
  • 4. RC、RR 快照读的不同
  • 5. 综上
  • 6. 总结
  • 后记

1. 隐藏字段

MySQL 的 InnoDB 会维护一系列不暴露给用户的隐藏字段,其中有3个用于实现快照读(非阻塞读)

  • DB_TRX_ID 最新一次修改本行记录的操作ID同一个事务,同一个操作要加以区分,这里指操作
  • DB_ROLL_PTR 回滚指针,存储的地址配合undo log
  • DB_ROW_ID InnoDB 无论有没有主键,都会通过该字段唯一标识一条记录

2. undo log 执行流程

  • Session A 修改 Field2 12 -> 32, 首先会对该行数据加上 写锁 (也叫排他锁)。DB_TRX_ID = NULL + 1,标识是 Session A 的一修改操作 ; 然后将 Field2 12 的信息拷贝至 undo log , 用回滚指针指向undo log 刚创建的记录。

如何避免mysql跨页_数据库

  • 并发情况下,Session B 修改 Field3 13 -> 45, 发现DB_TRX_ID = 1,证明已有其他事务操作过,利用DB_TRX_ID = DB_TRX_ID + 1 标识 Session B 的操作。多事务并发操作下,DB_TRX_ID递增,回滚指针 DB_ROLL_PTR 指向 undo log的数据,形成链表的数据结构。 越新开启的事务操作,DB_TRX_ID的值越大

3. undo log 与 read view(快照)

  • read view 用于做可见性判断,算法简略描述:
  1. 操作某条记录,先生成DB_TRX_ID。将自身记录的 DB_TRX_ID 与 当前活跃事务 的 DB_TRX_ID 进行比较
  2. 找到 DB_TRX_ID >= 当前活跃事务操作 的DB_TRX_ID 的上边界,比如 undo log 内有 DB_TRX_ID = 4, 3 ,2 ,1; 而当前的DB_TRX_ID 为 3,则上边界为3。
  3. 使用上边界,找到undo log 的记录,读取其所有Field的记录,当且仅当能看到并能修改上边界为3所暂存的记录。

4. RC、RR 快照读的不同

  • READ-COMMITTED一个事务下,RC 每条 dml 语句(增删改查)读数据都会创建一个新的 read view (快照),其DB_TRX_ID 每次读都 +1。
  • REPEATABLE-READ一个事务下,RR 第一条 dml 会对已存在的 undo log 按read view的规则创建一个快照。同一个事务下连续的多个select,也是只能读到同一个快照,即第一个快照的内容。一个事务下如果对记录进行了 update 操作, 快照也会被更新,再次select 时事务ID一致,但是快照不同

数据更新,快照更新,是符合当前事务下自己业务逻辑的 也就是 select + N*select + select 中间状态不会被别的事务改变 而 select + 其他事务的 update + 自己事务的 update + select 会合并其他事务的update,并更新快照

R 可以合并其他事务的 update 操作,看似与可重复读的矛盾之处 因为 RR 和 RC 下所有 update 操作都是当前读,不去读快照,读的是实时数据并加锁。 自己更新的数据,自己能感知到,看【高并发基础】理解 MVCC 及提炼实现思想

5. 综上

RR 的事务严格控制MVCC中的版本,让RR的事务下,不能读到未提交的数据只能读到提交了的数据。并且RR下重复快照读,数据是一致的,所以称之为可重复读的隔离级别。 为了保证当前业务的逻辑正确,RR可以借助当前读,刷新快照,此时再读就是不一样的数据。


6. 总结

undo log 实现了快照读的数据结构。 read view (快照)实现主要的快照算法。READ-COMMITTED 拥有细化到语句粒度的建立快照的能力。REPEATABLE-READ只拥有事务粒度的建立快照的能力