MySQL事务的实现原理
上次和字节的学长聊天模拟面试被问到这个问题,没答上来,一直以为事务就是事务= =,处理问问ACID还能问啥,后来百度了下,发现还真有事务的实现原理,看了很多博文,感觉还是没形成自己的知识体系,还是自己写一篇好了。
0 前言
首先,都知道事务的四大特性:原子性,一致性,隔离性,持久性。一般问事务的原理是问MySQL是怎么实现这四大特性的,值得一说的是,不是因为是事务,才实现了这四大特性,而是因为实现了四大特性,才是事务。额这么说,只是因为之前一直因为MySQL中存在事务这么一个实体,但事务仅仅是指一种机制罢了。
好的,以上是纯属是个人概念纠正,或许有些绕,可以忽略。接下看看MySQL是通过什么实现这四大特性的
- 事务的原子性是通过
undo log
来实现的 - 事务的持久性是通过
redo log
来实现的 - 事务的隔离性是通过 (读写锁+
MVCC
)来实现的 - 事务的一致性是通过原子性,持久性,隔离性来实现的
接下来先浅聊一下undo log
,redo log
,MVCC
把。
1 redo log——持久性
redo log(重做日志)是为了让已经提交了的事务对数据库中数据所作的修改永久生效,即使系统发生了崩溃,也可以将修改恢复出来。
redo log有许多的类型:MLOG_1BYTE
,MLOG_2BYTE
,MLOG_4BYTE
,MLOG_8BYTE
,MLOG_WRITE_STRING
。这里简单看一个类型的图了解下
日志格式这里其实很多可以讲的,详细的可以去看看《MYSQL是怎样运行的》,这里只是简单了解下。
Mini-Transaction
这个概念的出现是由于:当我们通过Insert语句插入一条数据时,不仅会修改页面的数据,还会更新聚簇索引,二级索引对应的页面,这些都是要记录到redo
日志中,这种情况下,就需要保证插入的原子性。
Mini-Transaction
就是指对底层页面的一次原子访问,简称mtr
。对于插入流程,的修改一次 Max Row ID
的值算是一个 Mini-Transaction
,向某个索引对应的 B+ 树中插入一条记录的过也算是一个 Mini-Transaction
。这其中是一种包含关系
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写入流程
很简单,就是当事务提交时,先将 redo log buffer 写入到 redo log file 进行持久化,待事务的commit操作完成时才算完成。称为预先日志持久化。
check point
这个概念真是困扰了我好久。这其实在为了辅助刷脏页而产生的概念
目的有三
- 缩短数据库的恢复时间
- buffer pool空间不够用时,将脏页刷新到磁盘
- redolog不可用时,刷新脏页
有着两种分类
sharp checkpoint
:完全检查点,数据库正常关闭时,会触发把所有的脏页都写入到磁盘上(这时候logfile的日志就没用了,脏页已经写到磁盘上了)。 1、完全检查点,发生在数据库正常关闭的时候。
2、在数据库在运行时不会使用
sharp checkpoint
,在引擎内部使用fuzzy checkpoint
,即只刷新一部分脏页,而不是刷新所有的脏页回磁盘。fuzzy checkpoint
:模糊检查点,部分页写入磁盘。 1、发生在数据库正常运行期间。
2、模糊检查点,不是sharp的就是模糊检查点(4种):
master thread checkpoint
、flush_lru_list checkpoint
、async/sync flush checkpoint
、dirty 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
记录的是对delete
和update
操作产生的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隔离级别。
实现时维护了:最大事务号,最小事务号,活跃事务集合(未提交事务)
对于当前事务的启动瞬间来说,一个数据版本的row trx_id
,有以下几种可能:
如果落在绿色部分,表示这个版本是已提交的事务或者是当前事务自己生成的,这个数据是可见的;
如果落在红色部分,表示这个版本是由将来启动的事务生成的,是肯定不可见的;
如果落在黄色部分,那就包括两种情况
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是怎样运行的》