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

水稻: 菜瓜,听说最近你在复习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实现

 

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

参考文章 

  • MySql-Undo及Redo详解 https://blog.csdn.net/aaa821/article/details/80645242
  • MySql MVCC 多版本并发控制 https://www.cnblogs.com/paulwang92115/p/12189487.html
posted @ 2020-05-28 22:22  渠成  阅读(374)  评论(0编辑  收藏