前言
说到数据库事务,大家脑子里肯定很容易蹦出一堆事务的相干常识,如事务的ACID个性,隔离级别,解决的问题(脏读,不可反复读,幻读)等等,然而可能很少有人真正的分明事务的这些个性又是怎么实现的,为什么要有四个隔离级别。
在之前的文章咱们曾经理解了MySQL中事务的隔离性的实现原理,明天就持续来聊一聊MySQL持久性的实现原理。
当然MySQL博大精深,文章疏漏之处在劫难逃,欢送批评指正。
阐明
MySQL的事务实现逻辑是位于引擎层的,并且不是所有的引擎都反对事务的,上面的阐明都是以InnoDB引擎为基准。
InnoDB读写数据原理
在往下学习之前,咱们须要先来理解下InnoDB是怎么来读写数据的。咱们晓得数据库的数据都是寄存在磁盘中的,而后咱们也晓得磁盘I/O的老本是很大的,如果每次读写数据都要拜访磁盘,数据库的效率就会非常低。为了解决这个问题,InnoDB提供了 Buffer Pool 作为拜访数据库数据的缓冲。
Buffer Pool 是位于内存的,蕴含了磁盘中局部数据页的映射。当须要读取数据时,InnoDB会首先尝试从Buffer Pool中读取,读取不到的话就会从磁盘读取后放入Buffer Pool;当写入数据时,会先写入Buffer Pool的页面,并把这样的页面标记为dirty,并放到专门的flush list上,这些批改的数据页会在后续某个时刻被刷新到磁盘中(这一过程称为刷脏,由其余后盾线程负责) 。如下图所示:
这样设计的益处是能够把大量的磁盘I/O转成内存读写,并且把对一个页面的屡次批改merge成一次I/O操作(刷脏一次刷入整个页面),防止每次读写操作都拜访磁盘,从而大大晋升了数据库的性能。
持久性定义
持久性是指事务一旦提交,它对数据库的扭转就应该是永久性的,接下来的其余操作或故障不应该对本次事务的批改有任何影响。
通过后面的介绍,咱们晓得InnoDB应用 Buffer Pool 来进步读写的性能。然而 Buffer Pool 是在内存的,是易失性的,如果一个事务提交了事务后,MySQL忽然宕机,且此时Buffer Pool中批改的数据还没有刷新到磁盘中的话,就会导致数据的失落,事务的持久性就无奈保障。
为了解决这个问题,InnoDB引入了 redo log来实现数据批改的长久化。当数据批改时,InnoDB除了批改Buffer Pool中的数据,还会在redo log 记录这次操作,并保障redo log早于对应的页面落盘(个别在事务提交的时候),也就是常说的WAL。若MySQL忽然宕机了且还没有把数据刷回磁盘,重启后,MySQL会通过曾经写入磁盘的redo log来复原没有被刷新到磁盘的数据页。
实现原理:redo log
为了进步性能,和数据页相似,redo log 也包含两局部:一是内存中的日志缓冲(redo log buffer),该局部日志是易失性的;二是磁盘上的重做日志文件(redo log file),该局部日志是长久的。redo log是物理日志,记录的是数据库中物理页的状况 。
当数据产生批改时,InnoDB不仅会批改Buffer Pool中的数据,也会在redo log buffer记录这次操作;当事务提交时,会对redo log buffer进行刷盘,记录到redo log file中。如果MySQL宕机,重启时能够读取redo log file中的数据,对数据库进行复原。这样就不须要每次提交事务都实时进行刷脏了。
写入过程
留神点:
- 先批改Buffer Pool,后写 redo log buffer。
- redo日志比数据页先写回磁盘:事务提交的时候,会把redo log buffer写入redo log file,写入胜利才算提交胜利(也有其余场景触发写入,这里就不开展了),而Buffer Pool的数据由后盾线程在后续某个时刻写入磁盘。
- 刷脏的时候肯定会保障对应的redo log曾经落盘了,也即是所谓的WAL(预写式日志),否则会有数据失落的可能性。
益处
事务提交的时候,写入redo log 相比于间接刷脏的益处次要有三点:
- 刷脏是随机I/O,但写redo log 是程序I/O,程序I/O可比随机I/O快多了,不须要。
- 刷脏是以数据页(Page)为单位的,即便一个Page只有一点点批改也要整页写入;而redo log中只蕴含真正被批改的局部,数据量十分小,有效IO大大减少。
- 刷脏的时候可能要刷很多页的数据,无奈保障原子性(例如只写了一部分数据就失败了),而redo log buffer 向 redo log file 写log block,是按512个字节,也就是一个扇区的大小进行写入,扇区是写入的最小单位,因而能够保障写入是必然胜利的。
先写redo log还是先批改数据
一次DML可能波及到数据的批改和redo log的记录,那它们的执行程序是怎么样的呢?网上的文章有的说先批改数据,后记录redo log,有的说先记录redo log,后改数据,那实在的状况是如何呢?
首先通过下面的阐明咱们晓得,redo log buffer在事务提交的时候就会写入redo log file的,而刷脏则是在后续的某个时刻,所以能够确定的是先记录redo log,后批改data page(WAL当然是日志先写啦)。
那接下来的问题就是先写redo log buffer还是先批改Buffer Pool了。要理解这个问题,咱们先要理解InnoDB中,一次DML的执行过程是怎么样的。一次DML的执行过程波及了数据的批改,加锁,解锁,redo log的记录和undo log的记录等,也是须要保障原子性的,而InnoDB通过MTR(Mini-transactions)来保障一次DML操作的原子性。
首先来看MTR的定义:
An internal phase ofInnoDB
processing, when making changes at the physical level to internal data structures during DML operations. A Mini-transactions (mtr) has no notion of rollback; multiple Mini-transactionss can occur within a single transaction. Mini-transactionss write information to the redo log that is used during crash recovery. A Mini-transactions can also happen outside the context of a regular transaction, for example during purge processing by background threads.见 https://dev.mysql.com/doc/ref...
MTR 是一个短原子操作,不能回滚,因为它自身就是原子的。数据页的变更必须通过MTR,MTR 会把DML操作对数据页的批改记录到 redo log里。
上面来简略看下MTR的过程:
- MTR初始化的时候会初始化一份 mtr_buf
- 当批改数据时,在对内存Buffer Pool中的页面进行批改的同时,还会生成redo log record,保留在mtr_buf中。
- 在执行mtr_commit函数提交本MTR的时候,会将mtr_buf中的redo log record更新到redo log buffer中,同时将脏页增加到flush list,供后续刷脏应用。在log buffer中,每接管到496字节的log record,就将这组log record包装一个12字节的block header和一个4字节的block tailer,成为一个512字节的log block,不便刷盘的时候对齐512字节刷盘。
由此可见,InnoDB是先批改Buffer Pool,后写redo log buffer的。
复原数据的过程
在任何状况下,InnoDB启动时都会尝试执行recovery操作。在复原过程中,须要redo log参加,而如果还开启了binlog,那就还须要binlog、undo log的参加。因为有可能数据曾经写入binlog了,然而redo log还没有刷盘的时候数据库就奔溃了(事务是InnoDB引擎的个性,批改了数据不肯定提交了,而binlog是MySQL服务层的个性,批改数据就会记录了),这时候就须要redo log,binlog和undo log三者的参加来判断是否有还没提交的事务,未提交的事务进行回滚或者提交操作。
上面来简略说下仅利用redo log复原数据的过程:
- 启动InnoDB时,找到最近一次Checkpoint的地位,利用Checkpoint LSN去找大于该LSN的redo log进行日志复原。
- 如果两头复原失败了也没影响,再次复原的时候还是从上次保留胜利的Checkpoint的地位持续复原。
Recover过程:故障复原蕴含三个阶段:Analysis,Redo和Undo。Analysis阶段的工作次要是利用Checkpoint及Log中的信息确认后续Redo和Undo阶段的操作范畴,通过Log修改Checkpoint中记录的Dirty Page汇合信息,并用其中波及最小的LSN地位作为下一步Redo的开始地位RedoLSN。同时修改Checkpoint中记录的沉闷事务汇合(未提交事务),作为Undo过程的回滚对象;Redo阶段从Analysis取得的RedoLSN登程,重放所有的Log中的Redo内容,留神这里也蕴含了未Commit事务;最初Undo阶段对所有未提交事务利用Undo信息进行回滚,通过Log的PrevLSN能够程序找到事务所有须要回滚的批改。具体见 http://catkang.github.io/2019...
什么是LSN?
LSN也就是log sequence number,也日志的序列号,是一个枯燥递增的64位无符号整数。redo log和数据页都保留着LSN,能够用作数据恢复的根据。LSN更大的示意所援用的日志记录所形容的变动产生在更前面。
什么是Checkpoint?
Checkpoint示意一个保留点,在这个点之前的数据页的批改(log LSN<Checkpoint LSN)都曾经写入磁盘文件了。InnoDB每次刷盘之后都会记录Checkpoint,把最新的redo log LSN 记录到Checkpoint LSN 里,不便复原数据的时候作为起始点的判断。
版权申明
转载请注明作者和文章出处
作者: X学生
https://segmentfault.com/a/1190000037671950
感觉不错的话请帮忙珍藏点赞~