MySQL InnoDB Update和Crash Recovery流程
MySQL InnoDB Update和Crash Recovery流程
- 概要信息
 - 首先介绍了Redo,Undo,Log Sequence Number (LSN),Checkpoint,Rollback Pointer (ROLL_PTR),Transaction ID (TRX_ID),Transaction Serialization Number(TRX_NO) 是什么?
 - 然后介绍了MySQL Update过程中发生了什么?Redo,Undo,双写之间如何配合,脏页何时刷新?
 - 最后介绍了Crash Recovery时如何做恢复?
 
1、InnoDB 术语和概念
我们首先来InnoDB的一些基本属于和概念,以便更好地理解下文中介绍的Update和Crash Recovery流程
1.1. InnoDB概述图

1.2. InnoDB 重要术语和概念
- 1.2.1. 什么是Redo?
 - 通常也会叫做"InnoDB log(s)",预先分配至少2个日志文件,第一个文件开头和最后一个文件结尾进行首尾相连以循环的方式重复使用。"Redo"的意思是在必要时(如:崩溃恢复时)可以使用Redo Log中的数据来重新应用到InnoDB数据文件中,使得InnoDB能够恢复到一个一致性状态
 - Redo Log 是一个预写日志(WAL),是一种用于在数据库或数据库所在主机发生崩溃时确保数据完整性的技术。Redo Log日志记录必须在数据实际更改(buffer pool中的脏页刷新到数据文件)之前写入磁盘。因此,对数据表做修改时,每个数据记录的修改都会写入Redo Log Buffer中(作为重做日志记录)。当一个页面的修改操作完成时,Redo Log Buffer将执行flush到Redo Log文件操作(但此时可能并未sync到磁盘)
 - 根据WAL日志先行原则,buffer pool中的脏页被刷新到数据文件中之前,需要确保对应LSN号的Redo Log先sync到磁盘文件中,Redo Log的刷盘机制以及脏页的刷盘机制确保了Redo Log总是先于脏页落盘。另外,如果系统参数innodb_flush_log_at_trx_commit设置为1,则每个事务提交时也会将Redo Log sync到磁盘文件中
 Redo Log 日志组结构

- 1.2.2. 什么是Undo?
 - 用于撤消(或还原)对InnoDB中存储的数据的变更及回滚事务,也用于实现多版本控制(mvcc),通过构建一致性视图(read view)实现对数据库的一致性读
 - 对数据库的每一次更改,Undo Log都会保存之前版本的数据,每个聚簇(PK)索引记录都有一个指向该修改记录之前版本数据的指针(称为“回滚指针”),每个Undo Log记录都会存储一个回滚指针指向之前版本的数据,另外,每个Undo Log的变更也必须记录到Redo Log中
 PS:Undo Log在共享表空间的基本结构
* 在共享表空间的第6个页存放了InnoDB的事务系统信息,其中也包含了Undo Log的一些系统信息

* InnoDB事务系统信息页结构

* InnoDB事务系统最多可以创建128个回滚段(MySQL 8.x版本除外),每个回滚段中都需要有一个单独的page来维护其拥有的undo solt(通常是每个回滚段中的第一个页),每个回滚段有1024个事务槽,每个事务槽指针都指向每个回滚段中的第一个UNDO_lOG页中的回滚段头


* Undo Log的数据存储在系统表空间的UNDO_LOG页中,下面分别是UNDO_LOG页结构、UNDO_LOG页中UNDO页头部和UNDO段头部、UNDO_LOG记录格式示意图



- 1.2.3. 什么是Log Sequence Number (LSN)?
 - 一个64位无符号整数,表示Redo Log系统中的时间点,也是事务写入Redo Log的字节总量,从日志初始化开始计数(数据库初始化安装时间点开始且单调递增)
 LSN不仅存在于Redo Log中,在每个数据页中都保存着一个LSN,在进行数据恢复时通过LSN做比较运算可以判断出每个数据页是否需要进行恢复操作
- 1.2.4. 什么是Checkpoint?
 - 一个时间点,由一个LSN值(Checkpoint LSN)表示的整型值,在Checkpoint LSN之前的每个数据页(buffer pool中的脏页)的更改都已经落盘(刷新到数据文件中),Checkpoint 完成后,在Checkpoint LSN之前的Redo Log就不再需要了
 - Checkpoint技术是为了解决:全量Redo Log恢复时间太长、buffer pool中的空闲页不够用时将脏页刷新到磁盘数据文件、Redo Log空间不够用时将脏页刷新到磁盘数据文件等问题
 Checkpoint方式有两种:Sharp Checkpoint和Fuzzy Checkpoint(又可根据不同的场景细分)
* Sharp Checkpoint:将所有的脏页刷回磁盘,数据库实例关闭时系统参数innodb_fast_shutdown设置为0,才需要把所有的脏页都刷回磁盘,刷脏时系统hang住
* Fuzzy Checkpoint:持续的每次只刷新一部分脏页到磁盘,数据库正常运行过程中都是使用这种方式刷脏,在InnoDB内部还可细分为如下几种:
** Master线程每秒/每十秒固定执行Checkpoint
** LRU list中空闲页不够时,触发Checkpoint从LRU list刷新脏页以释放足够的空闲页
** Redo Log空间不够时,触发Checkpoint从Flush list刷新脏页,Checkpoint执行完成之后,在这个位置之前的Redo Log不再需要(即,可以循环覆盖使用)
** 脏页太多达到脏页比例阀值(系统参数innodb_max_dirty_pages_pct和innodb_max_dirty_pages_pct_lwm控制脏页比例阀值),触发Checkpoint- 1.2.5. 什么是Rollback Pointer (ROLL_PTR)?
 - 一个由rollback segment number、page number和page offset组成的指针,指向Undo Log中包含之前版本数据的具体Undo Log日志记录
 可用于为任何数据记录回退到一个历史版本记录、可用于mvcc中重建旧版本记录、可用于事务回滚
- 1.2.6. 什么是Transaction ID (TRX_ID)?
 - 表示事务开始点的一个64位无符号整数
 - 每个事务的事务号增量增加
 - 事务号会写入聚簇索引的每个记录中
 最大事务号会写入系统表空间的TRX_SYS页
- 1.2.7. 什么是Transaction Serialization Number(TRX_NO) ?
 - 一个64位无符号整数,表示事务提交时的最大TRX_ID
 - TRX_NO在事务提交时会写入Undo Log Header
 TRX_NO可用于purge Undo Log中的旧版本记录
2、Update流程
- 2.1. 事务start(事务首次开启):
 - 为这个事务分配事务ID(TRX_ID),该事务ID可能被写入系统表空间的TRX_SYS页面中的最大的事务ID字段(Transaction ID)
* 如果系统表空间的TRX_SYS页面中的最大的事务ID字段被更新,则该更新会被记录到Redo Log中 根据分配的TRX_ID创建read view
- 2.2. 记录修改(每次只修改一行记录)
 - 分配Undo Log日志空间
 - 拷贝该记录修改之前的值到Undo Log中
 - 将Undo Log的修改记录写入Redo Log中
 - 在buffer pool中修改数据页,回滚段指针指向Undo Log中该记录之前的版本
 - 将该记录对应的数据页变更部分写入Undo Log中
 buffer pool中该记录修改之后的数据页被标记为"脏页"(需要刷新到磁盘的数据页)
- 2.3. 此时其他事务的修改会怎样?
 - 一旦记录被修改,即使没有提交,其他事务也可能会看到被修改后的记录,这依赖于他们的事务隔离级别而定
* 如果是RU隔离级别,则使用索引页读取最新版本记录
* 如果是RC隔离级别,则查找记录的最新提交版本
* 如果是RR隔离级别,则查找与其read view相对应的记录版本 任何需要使用索引页来读取比最新的版本记录旧的版本记录时,都必须使用Undo Log来重建之前的版本记录
- 2.4. 事务提交(显式和隐式提交)
 - 事务对应的Undo Log页被设置为"purge"(意味着当这个Undo Log页不再被任何其他事务引用时可以将其清除)
 - 将Undo Log的修改记录写入Redo Log中
 Redo Log Buffer刷新到磁盘(是否刷盘取决于系统变量innodb_flush_log_at_trx_commit的设置)
- 2.5. 后台线程刷脏(后台线程连续不断地根据不同触发机制触发刷新)
 - 查找最旧的“脏”页面(修改时间最早的页面)并将其添加到flush batch中
 - 确保在flush batch中中最新的LSN号已经写入到了Redo Log中且已经落盘
 - 如果开启了双写,则先将脏页刷新到双写缓冲区(并等待同步)
 - 将每个脏页从buffer pool中写入最终目的地:表空间文件中的
 PS:对于后台线程刷脏部分,执行刷新脏页时,与该脏页的事务是否提交无关,只需要确保该页对应LSN号的Redo Log记录落盘,而不会去判断事务的状态是否是提交还是未提交状态,因为,数据页结构中并没有地方单独记录事务的状态(即,无法判断事务是否提交),只是在每行数据中有记录事务号、回滚段指针(所以一个页中也可能包含多个事务的修改记录)。当需要对某个事务进行回滚时,重新从表空间中读取这个未提交的脏页,使用undo log中的反向数据进行反向修改,然后再重新刷脏。
- 2.6. 定期执行Checkpoint
 - 确保比Checkpoint 点更旧(比Checkpoint LSN小)的脏页已刷新到表空间文件,如果存在有比Checkpoint LSN大的脏页,则立即刷新脏页到数据文件中。说白了Checkpoint机制主要作用就是用于刷新脏页
 把Checkpoint LSN写到Redo Log Header中 (从这个Checkpoint LSN开始,之前的Redo Log记录不再需要)
- 2.7. 后台线程Purge(后台线程连续不断地根据需要定期执行Purge,包括Undo Log和历史链表)
 - 查找每个回滚段中不再需要的最旧的Undo Log
 - 实际上是从索引中删除任何带有删除标记的记录
 - 释放Undo Log页
 修剪history lists
3、Creash Recovery流程
- 3.1. 什么时候会进行Crash Recovery?
 - 实例崩溃之后重启
 - 使用一个备份还原(如:LVM 快照、xtrabackup备份)后
 在“快速”(innodb_fast_shutdown不为0值关闭实例)关闭实例后重新启动
- 3.2. 检测实例是不是干净地关闭的
 - 打开Redo Logs和系统表空间文件(ibdataN)
 - 读取并从中找到最大的Checkpoint LSN
 - 从最近的Checkpoint 开始往后扫描Redo Log
 如果能够找到Redo Log记录,说明还有数据页的更改没有刷新到数据文件上,启动Crash Recovery,使用Redo Log来恢复数据的一致性
- 3.3. 使用所有独立表空间的表名和表空间ID创建一个名称到ID的映射
 - 打开datadir下的所有.ibd文件
 - 在这些表空间文件的offset 0的页(FSP_HDR页)头读取其表空间ID(FSP_HDR页中FSP Header的前四个字节记录着表空间ID)
 - 将表空间ID与表名建立映射
 PS:
* page offset 0(FSP_HDR页)页结构

* FSP_HDR页中FSP Header的前四个字节记录着表空间ID

- 3.4. 损坏页修复(检查是否有不完整的页,如果有则使用Double Write Buffer进行修复)
 检查双写缓冲区中的所有128个页:
* 读取表空间中的每个“目标”页
* 如果页头和页尾的LSN不匹配或页面校验和无效,则使用双写缓冲区中的页进行还原
* 如果该页在双写缓冲区中的版本也被破坏,则server将crash- 3.5. 前滚Redo,回滚未提交事务
 - 事务系统初始化(回滚段初始化)
 - 从最近的Checkpoint 往后扫描到的Redo Log记录将被应用到各个数据文件中
 - 从Undo Log中恢复处于'ACTIVE'状态的事务,重新生成read view
 - 使用Undo Log回滚未提交的'ACTIVE'状态的事务
 处于PREPARE状态的事务,如果打开了binlog且在binlog有找到对应事务的日志则重新提交,否则回滚
4、参考资料
- 本文大部分为译文,原文PDF下载链接:
 - https://www.percona.com/live/mysql-conference-2013/sites/default/files/slides/InnoDB%20-%20A%20journey%20to%20the%20core%20-%20PLMCE%202013.pdf
 - https://www.percona.com/live/mysql-conference-2014/sites/default/files/slides/InnoDB%20-%20A%20journey%20to%20the%20core%20II.pdf
 - https://www.percona.com/live/mysql-conference-2015/sites/default/files/slides/InnoDB%20-%20A%20journey%20to%20the%20core%20III.pdf
 - 解析表空间工具:https://blog.jcole.us/2013/01/03/a-quick-introduction-to-innodb-ruby/
 - 深入了解InnoDB学习资料:https://blog.jcole.us/innodb/
 - MySQL · 引擎特性 · InnoDB 文件系统之文件物理结构:https://yq.aliyun.com/articles/51133
 - 关于2.5. 小节的PS部分,参考了oracle的资料,个人觉得MySQL也是同样的逻辑:http://blog.csdn.net/dba_waterbin/article/details/7823519
 - PS:本博客系个人博客,所发布大部分内容会延后于微信公众号"沃趣科技"和"DBGeeK" 以及其他任何与沃趣科技有合作的网站、公众号 发布
 
MySQL InnoDB Update和Crash Recovery流程的更多相关文章
- 转:InnoDB Crash Recovery 流程源码实现分析
		
此文章转载给登博的文章,给大家分享 InnoDB Crash Recovery 流程源码实现分析 Crash Recovery问题 本文主要分析了InnoDB整个crash recovery的源码处理 ...
 - mysql update语句的执行流程是怎样的
		
update更新语句流程是怎么样的 update更新语句基本流程也会查询select流程一样,都会走一遍. update涉及更新数据,会对行加dml写锁,这个DML读锁是互斥的.其他dml写锁需要等待 ...
 - insert buffer/change buffer double write buffer,双写 adaptive hash index(AHI) innodb的crash recovery innodb重要参数 innodb监控
		
https://yq.aliyun.com/articles/41000 http://blog.itpub.net/22664653/viewspace-1163838/ http://www.cn ...
 - MySQL InnoDB存储引擎大观
		
转的一篇文章作者:七把刀链接:https://www.jianshu.com/p/d4cc0ea9d097 MySQL InnoDB 引擎现在广为使用,它提供了事务,行锁,日志等一系列特性,本文分析下 ...
 - mysql innoDB 与 myISAM
		
转载文章 出处 http://www.pureweber.com/article/myisam-vs-innodb/ 使用MySQL当然会接触到MySQL的存储引擎,在新建数据库和新建数据表的时候都 ...
 - MySQL InnoDB特性:两次写(Double Write)
		
http://www.ywnds.com/?p=8334 一.经典Partial page write问题? 介绍double write之前我们有必要了解partial page write(部分页 ...
 - Mysql InnoDB三大特性-- double write
		
转自:http://www.ywnds.com/?p=8334 一.经典Partial page write问题? 介绍double write之前我们有必要了解partial page write( ...
 - MySQL InnoDB存储引擎事务的ACID特性
		
1.前言 相信工作了一段时间的同学肯定都用过事务,也都听说过事务的4大特性ACID.ACID表示原子性.一致性.隔离性和持久性.一个很好的事务处理系统,必须具备这些标准特性: 原子性(Atomicit ...
 - crash recovery
		
2016-07-02 17:56:07 5772 [Note] InnoDB: Database was not shutdown normally!2016-07-02 17:56:07 5772 ...
 
随机推荐
- .net敏捷开发框架 力软(learun) 让开发变的更简单
			
版本:6.1.6.2 体验地址:www.fishcmonkey.com 联系QQ:6539471
 - c#实战开发:用.net core开发一个简单的Web以太坊钱包 (六)
			
今天就来开发一个C# 版的简易钱包 先回顾以前的内容 c#实战开发:以太坊Geth 命令发布智能合约 (五) c#实战开发:以太坊Geth 常用命令 (四) c#实战开发:以太坊钱包快速同步区块和钱包 ...
 - Web Service 与WebAPI 的区别
			
Web Servise: web service 是一种跨编程语言和跨操作系统平台的远程调用技术. 所谓跨编程语言和跨操作系统平台,就是说服务器端程序采用Java编写,客户端程序则可以采用其他编程语言 ...
 - AJAX  实战【三级联动】分析
			
使用 AJAX 对全国地名进行选取 <%@ Page Language="C#" AutoEventWireup="true" CodeFile=&quo ...
 - 数据机构-折半查找法(二分查找法)-Python实现
			
Python实现二分查找法(基于顺序表) class List: elem=[] #存储顺序表元素 last=-1 #设置初始为-1 SeqList = List() #创建一个顺序表 print(& ...
 - GCC链接的几个注意点
			
库文件依赖顺序 GCC在链接时对依赖库的顺序是敏感的,被依赖的库必须放在后面,比如liba.a依赖libb.a,必须写成liba.a libb.a,否则链接将出错.在库比较多依赖关系比较复杂或者相互依 ...
 - Minitab系列 前言
			
Matlab 在有些时候太过去强大,强大到几乎所有的数学问题都可以解决. 在这里,Matlab就像数学版的Vim,对于一般的使用者还是不想编程,想要更加的直接直观的数学处理工具,这里向大家介绍mini ...
 - C# 填充Excel
			
1.添加引用 Microsoft.Office.Interop.Excel; 2.使用命名空间 using Microsoft.Office.Interop.Excel; 3.填充EXCEL单元格方法 ...
 - HDU5037(SummerTrainingDay01-C)
			
Frog Time Limit: 3000/1500 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)Total Subm ...
 - Python 映射
			
python中的反射功能是由以下四个内置函数提供:hasattr.getattr.setattr.delattr,改四个函数分别用于对对象内部执行:检查是否含有某成员.获取成员.设置成员.删除成员. ...