【MySQL】事务的隔离级别是如何实现的

Author Avatar
lucky 2020年09月28日
  • 在其它设备中阅读本文章

原文地址:https://www.cnblogs.com/nightOfStreet/p/12977291.html


水稻: 菜瓜,听说最近你在复习 MySQL 方面的知识,想请教一下 MySQL 的事务?

菜瓜:嗯,最近刚刚看到。事务指的是 MySQL 中不可拆分的业务单元,具有 ACID 的属性。

水稻: ACID 我知道啊,但是不太懂他的实现,你能说和我聊聊事务在数据库底层是怎么实现的吗?

菜瓜:据我了解,不同的特性底层的实现不一样,主要依赖两种日志和锁来实现

  • 先说持久性:我们知道数据的操作会先在内存中完成,那么事务提交后如何保证一定能持久化到磁盘呢
    • redo log: 事务在提交前对数据的修改会先写到 redo log 中,如果返回事务已提交成功,那么表示 redo log 已经记录完成。redo log 也有缓冲区,redo log 的内存缓冲区大小和磁盘扇区的大小 512 字节一致,不会出现掉电易失的情况。另外 redo log 记录的是物理变化,体积很小,且 redo log 写磁盘是顺序 IO,极快~ 丝滑
    • redo log 和 binlog 区别:一个是用于做持久化,另一个用作数据恢复和复制
  • 原子性,指的是被事务包裹的一组操作要么全部成功,要么全部失败。不会存在执行了一部分,另一部分不执行的情况
    • undo log: MySQL 使用 undo log 实现操作回滚。事务开启后执行的命令都会有一条对应反向的逻辑日志计入 undo 日志文件中(譬如 insert 就会有一条 delete)。undo log 的持久化会被记录在 redo log 中(利用 redo log 速度快的特性)。一旦发生错误或者回滚的时候,利用 undo 就可以操作回去
      水稻: 那还有一致性和隔离性呢?

菜瓜:一致性和隔离性可以放在一起说,隔离级别的选择就是一致性和隔离性的权衡

  • 实现多个事务之间的隔离。一种是锁,另一种是 mvcc 机制。
    水稻:锁我知道,mvcc 是什么?

菜瓜:我们把数据库的读操作分为两类,一是当前读,使用锁机制;一是快照读,使用 mvcc

  • 当前读
    • 数据的修改操作(insert update delete)和查询时显示加锁 select(查询条件后加上 lock in share mode & for update)
    • 会锁住要读取的数据以保障数据的一致
  • 快照读 使用的是 mvcc 机制,就是多版本并发控制。
    • 除当前读之外,普通的 select 查询为快照读,顾名思义,就是读取的是一个快照版本,以隔离多个事务之间的数据

水稻:能不能仔细说说这个 mvcc

菜瓜:可以,它的实现还是依赖 undo log 来做的

  • 在 RR RC 两种级别下使用。其他两种不需要实现隔离
  • 你肯定听说过 mysql 在 RR 级别下解决了幻读问题,就是依赖这个来做的
    • 简单来说就是,MySQL 维护了一个记录活跃事务 id 的列表 readview
    • undo log 是怎么记录的呢。举个栗子
      • innodb 的表中存在三个额外的隐藏字段,分别是编辑该条记录的事务 id,更改前的 undo log 的回滚指针,还有一个对我们这个分析不太重要
      • 如果有事务对该记录做了变更,事务 id 会更新,同时 undo log 里面会产生新记录,回滚指针字段指向最新的 undo log 链
      • 通过比较当前事务 id 和 readview 中其他事务的 id 大小来决定自己读取的数据是哪个版本的 undo log 记录
        • 如果当前事务 id 比 readview 中的都小,就说明该条记录没有被其他事务更改。直接读取
        • 如果当前事务 id 比 readview 中的都大,沿着 undo log 链能找到最小事务 id 指向的 undo log,该数据为稳定数据
      • RR 级别下利用该机制避免了幻读
      • RC 级别下每次都会读取数据的最新记录

总结:

  1. 事务的持久性和原子性由 Redo log 和 Undo log 实现
  2. 隔离性和一致性的权衡由锁机制和 MVCC 实现

部分内容为自己猜想,如有错误,欢迎指正!

评论已关闭