MySQL事务的实现原理

​ 上次和字节的学长聊天模拟面试被问到这个问题,没答上来,一直以为事务就是事务= =,处理问问ACID还能问啥,后来百度了下,发现还真有事务的实现原理,看了很多博文,感觉还是没形成自己的知识体系,还是自己写一篇好了。

0 前言

​ 首先,都知道事务的四大特性:原子性,一致性,隔离性,持久性。一般问事务的原理是问MySQL是怎么实现这四大特性的,值得一说的是,不是因为是事务,才实现了这四大特性,而是因为实现了四大特性,才是事务。额这么说,只是因为之前一直因为MySQL中存在事务这么一个实体,但事务仅仅是指一种机制罢了。

​ 好的,以上是纯属是个人概念纠正,或许有些绕,可以忽略。接下看看MySQL是通过什么实现这四大特性的

  • 事务的原子性是通过 undo log 来实现的
  • 事务的持久性是通过 redo log 来实现的
  • 事务的隔离性是通过 (读写锁+MVCC)来实现的
  • 事务的一致性是通过原子性,持久性,隔离性来实现的

​ 接下来先浅聊一下undo logredo logMVCC把。

1 redo log——持久性

​ redo log(重做日志)是为了让已经提交了的事务对数据库中数据所作的修改永久生效,即使系统发生了崩溃,也可以将修改恢复出来

​ redo log有许多的类型:MLOG_1BYTEMLOG_2BYTEMLOG_4BYTEMLOG_8BYTEMLOG_WRITE_STRING。这里简单看一个类型的图了解下

图片来自网络

​ 日志格式这里其实很多可以讲的,详细的可以去看看《MYSQL是怎样运行的》,这里只是简单了解下。

Mini-Transaction

​ 这个概念的出现是由于:当我们通过Insert语句插入一条数据时,不仅会修改页面的数据,还会更新聚簇索引,二级索引对应的页面,这些都是要记录到redo日志中,这种情况下,就需要保证插入的原子性。

Mini-Transaction就是指对底层页面的一次原子访问,简称mtr。对于插入流程,的修改一次 Max Row ID 的值算是一个 Mini-Transaction,向某个索引对应的 B+ 树中插入一条记录的过也算是一个 Mini-Transaction。这其中是一种包含关系

image-20220418184558198

1.1 redo log实现持久性

Redo log结构

Redo log 可以分为以下两个部分:

  • 一是Redo log Buffer,易失的,保存在内存中
  • 二是Redo log File,非易失的,保存在磁盘中

写入Redo log时机

  • redo日志比数据页先写回磁盘
  • 在数据页修改完成之后,在脏页刷出磁盘之前,写入redo日志。注意的是先修改数据,后写日志
  • 聚集索引、二级索引、undo页面的修改,均需要记录Redo日志。

redo log刷盘时机

  • log buffer 空间不足时
  • 后台线程不停的刷刷刷
  • 正常关闭服务器时
  • 做所谓的 checkpoint

redo log写入流程

image-20220418191903334

​ 很简单,就是当事务提交时,先将 redo log buffer 写入到 redo log file 进行持久化,待事务的commit操作完成时才算完成。称为预先日志持久化

check point

​ 这个概念真是困扰了我好久。这其实在为了辅助刷脏页而产生的概念

目的有三  

  1. 缩短数据库的恢复时间
  2. buffer pool空间不够用时,将脏页刷新到磁盘
  3. redolog不可用时,刷新脏页

有着两种分类

  1. sharp checkpoint:完全检查点,数据库正常关闭时,会触发把所有的脏页都写入到磁盘上(这时候logfile的日志就没用了,脏页已经写到磁盘上了)。

    ​ 1、完全检查点,发生在数据库正常关闭的时候。

    ​ 2、在数据库在运行时不会使用sharp checkpoint,在引擎内部使用fuzzy checkpoint,即只刷新一部分脏页,而不是刷新所有的脏页回磁盘。

  2. fuzzy checkpoint:模糊检查点,部分页写入磁盘。

    ​ 1、发生在数据库正常运行期间。

    ​ 2、模糊检查点,不是sharp的就是模糊检查点(4种):master thread checkpointflush_lru_list checkpointasync/sync flush checkpointdirty page too much checkpoint。

2 undo log——原子性

​ undo log(回滚日志),用于记录数据被修改前的信息。它实际是做了相反的工作,比如一条INSERT ,对应一条 DELETE,对于每个UPDATE,对应一条相反的 UPDATE,将修改前的行放回去。undo log用于保障了事务的原子性

​ 在InnoDB存储引擎中,undo log分为:

  • insert undo log
  • update undo log

insert undo log是指在insert操作中产生的undo log,因为insert操作的记录,只对事务本身可见,对其他事务不可见。故该undo log可以在事务提交后直接删除,不需要进行purge操作。

update undo log记录的是对deleteupdate操作产生的undo log,该undo log可能需要提供MVCC机制,因此不能再事务提交时就进行删除。提交时放入undo log链表,等待purge线程进行最后的删除。

purge线程两个主要作用是:清理undo页清除page里面带有Delete_Bit标识的数据行。在InnoDB中,事务中的Delete操作实际上并不是真正的删除掉数据行,而是一种Delete Mark操作,在记录上标识Delete_Bit,而不删除记录。是一种”假删除”,只是做了个标记,真正的删除工作需要后台purge线程去完成。

3 MVCC——隔离性

​ 先说说四大隔离级别:

  • 读未提交:一个事务还没提交时,它做的变更就能被别的事务看到。
  • 读提交:一个事务提交之后,它做的变更才会被其他事务看到。
  • 可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
  • 串行化:顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

​ 其中,读提交和可重复读在实现上会用到一个ReadView,访问的时候以ReadView的逻辑结果为准

MVCC

​ MVCC是指多版本并发控制机制,用于支持RC和RR隔离级别。

​ 实现时维护了:最大事务号,最小事务号,活跃事务集合(未提交事务)

image-20220418200912326

​ 对于当前事务的启动瞬间来说,一个数据版本的row trx_id,有以下几种可能:

  1. 如果落在绿色部分,表示这个版本是已提交的事务或者是当前事务自己生成的,这个数据是可见的;

  2. 如果落在红色部分,表示这个版本是由将来启动的事务生成的,是肯定不可见的;

  3. 如果落在黄色部分,那就包括两种情况

    a. 若 row trx_id在集合中,表示这个版本是由还没提交的事务生成的,不可见;

    b. 若 row trx_id不在集合中,表示这个版本是已经提交了的事务生成的,可见。

​ 这一部分讲得比较简单,其实实现隔离不仅仅用到了MVCC,还有MYSQL锁,但之前写过一篇有关锁的博文了,这里就不多赘述,而且感觉隔离性的实现也不是很难。

4 总结

​ 总算,对事务的原理了解了些,以后面试又可以和面试官多BB聊天了,其实这些概念一直都了解过,但之前没理解事务的原理是指这些。

参考

[MySQL事务的实现原理](https://blog.csdn.net/qq_42764468/article/details/108572936#:~:text=MySQL事务的实现原理 事务的原子性是通过 undo log,来实现的 事务的持久性性是通过 redo log 来实现的)

MySQL实战45讲

《MySQL是怎样运行的》


MySQL事务的实现原理
https://2w1nd.github.io/2022/04/18/数据库/MySQL事务的实现原理/
作者
w1nd
发布于
2022年4月18日
许可协议