MySQL的事务实现原理介绍:undo log、redo log、checkpoint和LSN

参考博客1(建议先通读该博客)介绍了MySQL通过Undo+Redo Log的机制实现事务的原子性、一致性和持久性(关于事务的隔离性是通过锁机制来保障的,请参考我的另一篇博文MySQL常见的七种锁详细介绍)。以下为博客1给出的Undo log和Redo log实现的简化示例:

- 用Undo Log实现原子性和持久化的事务的简化过程- 用Undo + Redo实现原子性和持久化的事务的简化过程
假设有A、B两个数据,值分别为1,2。假设有A、B两个数据,值分别为1,2.
A.事务开始.A.事务开始.
 B.记录A=1到undo log.B.记录A=1到undo log.
C.修改A=3.C.修改A=3.
 D.记录B=2到undo log.D.记录A=3到redo log.
E.修改B=4.E.记录B=2到undo log.
F.将undo log写到磁盘.F.修改B=4.
G.将数据写到磁盘.G.记录B=4到redo log.
H.事务提交H.将redo log写入磁盘.
I.事务提交

这里重点对博客中提到的Redo Log进行故障恢复的机制进一步讨论。这里先摘抄原文中的两句话:

第一,在重做redo log时,不关心事务性,在恢复时,没有BEGIN,也没有COMMIT或ROLLBACK的行为。尽管事务ID等事务相关的内容会记入Redo Log,这些内容只是被当作要操作的数据的一部分;

第二,在进行恢复时,重做所有事务,包括未提交的事务和回滚了的事务,然后通过undo Log回滚那些未提交的事务。

第一句话没毛病,但第二句话的后半句则不准确。事实上,在进行故障恢复时,确实是先重做所有事务(即执行redo log操作,剔除其中的undo log),但随后执行undo log时,并不会回滚所有未提交的事务,而只会回滚还没有写binlog的事务,对于已经写了binlog的事务,还是会进行提交,以确保master和消费binlog的slave数据在故障恢复后还能保持数据一致。

为了理解上面这段话,我们需要知道redo log和binlog采用的是两阶段提交(详见参考博客7),即binlog 的完成在redo log 的prepare和commit之间。假设一个事务已经落了binlog,但redo log还处于prepare状态,那么故障恢复的时候,对比undo log和binlog中的事务id,如果某个事务id只在undo log中存在,而在binlog中不存在,说明该事务需要被回滚,否则需要提交(当然,因为两阶段提交都是基于内存操作,并没有直接持久化到磁盘,而binlog和redolog 的落盘相互独立,就有可能会出现某个事务id在binlog中存在,而在undo log中不存在,这种场景可能导致故障恢复后,原来的master比slave少一个事务,不过可以通过调整innodb_flush_log_at_trx_commit、sync_binlog等参数解决,不过性能就会受到一定程度的影响)。

完整的故障恢复过程如下(详见博客14):

第一步,将redo log中的事务恢复到磁盘;

第二步,根据最后一个binlog文件,为啥不是所有binlog文件呢?因为每一个binlog文件切换的时候,都会确保当前binlog文件的所有操作已落盘,所以只需要考虑最后一个binlog文件。跟进最后一个binlog文件,获取所有可能没有提交事务的xid列表;

第三步,根据undo log中的 insert_undo_list,upddate_undo_list事务链,构建undo_list,再根据undo_list构建未提交事务链表;

第四步,从未提交事务链表中,提取出xid,凡是存在于第二步的xid列表中的,则需要提交,不存在的,则回滚。

另外,参考博客2提到两个重要的名词:检查点(checkpiont)和幂等性(idempotence)问题。首先我们来说说幂等性。博客中要求日志文件中的操作记录应该具有幂等性,原因是在故障恢复中日志中的记录可能会重复执行多次,如果操作记录不满足幂等性,会造成数据错误。举例来说就是,我们在写增删改等语句的时候,可能并没有明确受影响的记录范围,比如我们在写insert的语句的时候,往往不会限定自增主键id的值,但当Mysql将我们的insert语句记录到redo log日志中时,一定会明确其id值(实际上redo log中存储的是更新后的物理数据页,即物理日志),否则如果执行多次恢复就无法保证幂等性(当然对于insert还需要忽略因记录已经存在而导致插入失败的错误)。再比如对update操作,我们在写update语句的时候,可能会写成在原值的基础上增加或减少一个数值,但redo log中的记录也是变更后的最终值,而不是增量值。除此之外,还有一点非常重要:我们在做多次恢复操作期间,数据库是不能对外提供服务的,否则恢复操作可能会因数据变更而导致数据出错,幂等性也就无从谈起。

再来说说checkpoint。要说checkpoint就不得不提LSN(Log Sequence Number)。我们先了解下为什么要有这两个概念。参考博文3中提到:

InnoDB采用Write Ahead Log策略来防止宕机数据丢失,即事务提交时,先写重做日志,再修改内存数据页,这样就产生了脏页。既然有重做日志保证数据持久性,查询时也可以直接从缓冲池页中取数据,那为什么还要刷新脏页到磁盘呢?如果重做日志可以无限增大,同时缓冲池足够大,能够缓存所有数据,那么是不需要将缓冲池中的脏页刷新到磁盘。但显然,服务器的内存是有限的,缓冲池不能缓存全部数据,而且重做日志无限增大,宕机后重做全部日志则的恢复时间会过长。前面提到过,恢复期间,数据库是不能对外提供服务的。为了缩短数据库恢复时间,我们需要一种机制,定期地将日志和数据页持久化到磁盘。该机制还要记录哪些日志在数据恢复时需要执行,哪些已经不需要执行。该机制就是checkpoint,而checkpoint是通过LSN实现的。具体我们看参考博文4:

checkpoint是log日志对数据页刷新到磁盘的操作的检查点,通过LSN号保存记录,作用是当发生宕机等crash情况时,再次启动时会查询checkpoint点,将该检查点之后发生的事务修改恢复到磁盘。

InnoDB引擎通过LSN来标记版本,LSN是日志空间中每条日志的结束点,用字节偏移量来表示。每个数据页有LSN,redo log也有LSN,Checkpoint也有LSN。可以通过命令show engine innodb status来观察。

mysql> show engine innodb status\G;
---
LOG
---
Log sequence number 10623965866
Log flushed up to   10623965866
Pages flushed up to 10623965866
Last checkpoint at  10623965857
0 pending log flushes, 0 pending chkp writes
13 log i/o‘s done, 0.81 log i/o‘s/second

Last checkpoint at就是系统最后一次刷新buffer pool中页数据到磁盘的checkpoint,checkpoint是和redo log进行关联操作的,也就记录在redo log中,checkpoint记录在redo log第一个文件的头部,存储两个值循环更替修改。

LSN(log sequence number)日志序列号,5.6.3之后占用8字节,LSN主要用于发生crash时对数据进行recovery,LSN是一个一直递增的整型数字,表示事务写入到日志的字节总量

LSN不仅只存在于重做日志中,在每个数据页头部也会有对应的LSN号,该LSN记录当前页最后一次修改的LSN号,用于在recovery时对比重做日志LSN号决定是否对该页进行恢复数据。前面说的checkpoint也是有LSN号记录的,LSN号串联起一个事务开始到恢复的过程。

到此我们明白了checkpoint和LSN的工作机制:日志空间中的每条日志对应一个LSN值,而在数据页的头部也记录了当前页最后一次修改的LSN号每次当数据页刷新到磁盘后,会去更新日志文件中的checkpoint,以减少需要恢复执行的日志记录。极端情况下,数据页刷新到磁盘成功后,去更新checkpoint时如果宕机,则在恢复过程中,由于checkpoint还未更新,则数据页中的记录相当于被重复执行,这个时候,前面介绍的幂等性的作用就体现出来了。

参考博客:

1. MySQL redo与undo_pursuer_chen的博客-CSDN博客_mysql redo undo MySQL redo与undo

2. 理解数据库中的undo日志、redo日志、检查点_kobejayandy的博客-CSDN博客 理解数据库中的undo日志、redo日志、检查点

3. InnoDB Redo Flush及脏页刷新机制深入分析_Mr_HanSong的博客-CSDN博客 InnoDB Redo Flush及脏页刷新机制深入分析

4. http://www.mamicode.com/info-detail-1264451.html mysql 之 checkpoint和LSN详解

5. 面试官:MySQL事务是怎么实现的_Sicimike的博客-CSDN博客_mysql如何实现事务 MySQL事务是怎么实现的

6. savepoint原理 savepoint原理

7. mysql日志系统 SQL 逻辑日志 物理日志 - binyang - 博客园   重要

8.redo&undo日志解析_piriineos的博客-CSDN博客  redo&undo日志解析

9.详细分析MySQL事务日志(redo log和undo log)  重要

10.InnoDB如何保证redolog的完整性? - 知乎 InnoDB如何保证redolog的完整性?

11.Redo Log——第一篇 - 简书  Redo Log——第一篇

12.MySQL · 引擎特性 · InnoDB redo log_bohu83的博客-CSDN博客 MySQL · 引擎特性 · InnoDB redo log 

13.MySQL · 引擎特性 · InnoDB undo log_bohu83的博客-CSDN博客 MySQL · 引擎特性 · InnoDB undo log

14.梳理下MySQL崩溃恢复过程 - 苏家小萝卜 - 博客园 MySQL崩溃恢复过程  重要

15、mysql日志系统 SQL 逻辑日志 物理日志  重要

16、MySQL原子性与持久性的保证(undo log, redo log与binlog)_mu6y的博客-CSDN博客_mysql原子性 重要

17、解析MySQL binlog --(6)XID_EVENT、ROTATE_EVENT及stop_yanzongshuai的专栏的技术博客_51CTO博客  解析MySQL binlog --(6)XID_EVENT、ROTATE_EVENT及stop  :当事务提交时,不论是statement还是row格式的binlog都会添加一个XID_EVENT作为事务的结束。该事件记录了该事务的ID。在mysql进行崩溃恢复时根据binlog中提交的情况来决定是否提交redo log中处于prepared状态的事务。

18、MySQL 重要参数 innodb_flush_log_at_trx_commit 和 sync_binlog - klvchen - 博客园   MySQL 重要参数 innodb_flush_log_at_trx_commit 和 sync_binlog   重要


19、详解MySQL|深度解析MySQL 5.7的Loss-Less半同步复制技术-爱可生_slave

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值