【MySQL】InnoDB 如何避免脏读和不可重复读

20 篇文章 0 订阅
7 篇文章 0 订阅

前言

阅读建议:快速略读本文后,切换到更严谨的文章:【高并发基础】理解 MVCC 及提炼实现思想

阅读本文必须提前了解:当前读和快照读的区别

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 刚创建的记录。

在这里插入图片描述

  • 并发情况下,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只拥有事务粒度的建立快照的能力

后记

更完整的事务隔离博客(含可运行代码)

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值