目录
关于Redo Log 的一些其余问题
小结
本篇承接自InnoDB存储引擎<五>的内容
InnoDB 存储引擎<五>
关于Redo Log 的一些其余问题
4.不同⽇志类型对应了哪些操作?
分析过程:1.⽇志类型总体可以分为三⼤类,分别是:⽤于数据⻚的⽇志类型、⽤于表空间⽂件的⽇志类型和提供额外信息的⽇志类型,不同的⽇志类型对应的⽇志内容也不尽相同,⽽进⾏DML操作时,⼤多数RedoLog属于⽤于数据⻚的⽇志类型2.属于⽤于数据⻚的⽇志类型中的⼏种最常⻅数据操作所对应的⽇志类型如下:(1)MLOG_WRITE_STRING = 30 , type 对应的值为30,表⽰在⻚⾯的某个偏移量处写⼊⼀个字符串,由于字符串的⻓度不固定,需要⽤到⼀个表⽰⻓度的区域记录,此时⽇志内容格式 .如下图所⽰:
(2)MLOG_4BYTES = 4, type 对应的值为4, 这种类型应⽤于对固定⻓度值的修改,⽐如修
改整型字段,由于⻓度固定,所以⽤于表⽰⻓度的区域可以省略,从⽽尽可能的减少空间使⽤,此时⽇志内容格式如下图所⽰:
(3)类似的类型还有 MLOG_1BYTE = 1 , MLOG_2BYTES = 2 , MLOG_8BYTES = 8 分别表⽰固定修改1字节、2字节、8字节的数据,⽇志格式与 MLOG_4BYTES = 4 相同(4)还有其他⼀些⽇志类型,⽐如:MLOG_REC_INSERT_8027 = 9 ,表⽰写⼊⼀条⾏格式为⾮紧凑型的记录,对应的⾏格式为RedundantMLOG_COMP_REC_INSERT_8027 = 38 ,表⽰写⼊⼀条⾏格式为紧凑型的记录,对应的格式为Compact、Dynamic和CompressedMLOG_REC_UPDATE_IN_PLACE_8027 = 13 ,表⽰更新⼀条记录MLOG_REC_DELETE_8027 = 14 ,表⽰从数据⻚中删除⼀条记录MLOG_LIST_END_DELETE_8027 = 15 ,表⽰从索引⻚中删除最后⼀条记录MLOG_LIST_START_DELETE_8027 = 16 ,表⽰从索引⻚中删除第⼀条记录MLOG_LIST_END_DELETE_8027 和 MLOG_LIST_START_DELETE_8027 配合可以删除索引⻚中⼀个范围的记录,⽽不⽤记录每⼀条记录的删除⽇志MLOG_PAGE_CREATE = 19 ,表⽰创建⼀个索引⻚,这个类型是关于⻚的类型3. 属于⽤于表空间⽂件的⽇志类型:MLOG_FILE_CREATE = 33 ,表⽰创建⼀个.ibd表空间⽂件MLOG_FILE_RENAME = 34 ,表⽰重命名⼀个表空间⽂件MLOG_FILE_DELETE = 35 ,表⽰删除⼀个表空间⽂件4.属于提供额外信息的⽇志类型:MLOG_MULTI_REC_END = 31 ,只由⼀个字节的 Type 构成,⽤于标识⼀个 Mini-Transaction(MTR)的结尾。解答问题不同的⽇志类型对应的⽇志内容和作⽤各不相同1.⽤于数据⻚的⽇志类型主要记录数据⻚的修改,⽐如创建和删除数据⻚,以及对数据的增删改查操作2.⽤于表空间⽂件的⽇志类型 :主要记录对表空间⽂件的修改3.提供额外信息的⽇志类型:主要标记⼀个Mini-Transaction的结尾衍⽣问题如果⼀个DML操作修改了表中的多个字段,⽇志如何表⽰?通常情况下,⼀个DML操作会修改表中的多个字段,也可能修改多条记录,对于正常的增删改对应不同的⽇志类型,对应⽇志所包含的主要信息如下图所⽰:
1.新增操作:主要包含数据⻚内的偏移量,主键信息,新增的字段个数,每个字段的内容的实际⻓度,具体的内容等2.删除操作:主要包含数据⻚内的偏移量,主键信息、旧事务的Id,旧roll_pointer,是否删除标识3.更新操作:主要包含数据⻚内的偏移量,主键信息、旧事务的Id,旧roll_pointer,被更新字段的数据,每个被更新字段的实际⻓度,更新的具体内容小结:Redo Log的作⽤是把事务在执⾏过程中对数据库所做的所有修改都记录下来,以便在系统崩溃重启后可以把事务所做的修改都恢复出来
5.什么是Mini-Transaction?
分析过程:
1.DML操作会对数据⻚产⽣什么样的影响?
以⼀个Insert操作为例,对数据⻚的影响⼀般分为两种情况:(1)如果写⼊记录所在的数据⻚空间充⾜,⾜够存储⼀条将要写⼊的记录,那么就可以直接写⼊,如下图所⽰:
(2)如果写⼊的数据⻚空间不充⾜,⽆法放下这条记录,由于在数据⻚中真实数据是按主键顺序排列的,那么就要新建⼀个数据⻚,对原来的数据进⾏调整,把⼀部分数据复制到新的数据⻚中,以便在⽬标数据⻚上留出⾜够的空间来保存即将写⼊的记录,此时对应的⽰意图如下所⽰:
(3)通过以上两种情况下插⼊⼀条记录的分析可以看出,当数据⻚空间充⾜的情况下可以直接写⼊数据,并记录⼀条对应RedoLog即可(4)当数据⻚空间不充⾜⽆法放下这条记录的情况下,会创建⼀个新数据⻚,同时还有数据的复制和写⼊,索引树⾮叶⼦节点上修改,在实际的执⾏过程中还有对表空间中段、区中统计信息的修改等等,这意味⼀个简单的Insert操作有会产⽣很多条RedoLog。2. 在记录RedoLog时服务器崩溃了导致⽇志不完整怎么办?
那么这时有⼀个问题需要考虑,试想⼀下如果执⾏这⼀系统操作的时候,RedoLog只记录了⼀半服务器就崩溃了,那么当服务器重启的时候如果按照RedoLog进⾏恢复,得到的结果肯定是错误的,所以在记录RedoLog的时候要保证⼀个DML所对应的⼀系列⽇志必须是完整的才可以执⾏恢复操作,否则就不执⾏恢复,那么怎么才能标记DML操作对应的日志是完整的?3. Mini-Transaction的定义
在执⾏DML操作的过程中,每⼀个对数据⻚的修改都会记录⼀条RedoLog,这些⽇志会被顺序记录下来,并在这组⽇志的最后加⼀条特殊的⽇志标识作为⼀个MRT的结尾,这条特殊的⽇志结构⾮常简单,只有⼀个 TYPE 字段,类型为 MLOG_MULTI_REC_END = 31 ,也就是⽇志分类中的提供额外信息的⽇志类型,⼀个MTR对应的⽇志组,如下图所⽰:
2.如果⼀个MTR中只有⼀条⽇志是否可以优化?
(1)当然可以,如果⼀个MTR只有⼀条⽇志,直接在这条⽇志后加⼀个类型为MLOG_MULTI_REC_END = 31 的标识可以做为MTR的结尾,但这样做有点浪费空间;(2)InnoDB为了尽可能的节省空间,在MTR只有⼀条⽇志的情况下,做了⼀个优化。通过上⾯的介绍了解了⽇志类型虽然很多,但也只有⼏⼗种,⽽⽤来表⽰⽇志类型的 TYPE 字段⻓度为 1BTYE ,⽽这 1BTYE 中只⽤7个⽐特位,代表整数127,就完全可以表⽰所有的⽇志类型,于是省出来⼀个⽐特位就可以⽤来表⽰当前MTR包含⼀条还是⼀组RedoLog,也就是说如果 TYPE 字段的第⼀个⽐特位为 1 ,表⽰MTR只包含⼀条RedoLog,为 0 表⽰MTR包含⼀组RedoLog,如下图所⽰:
3.事务与Mini-Transaction是什么关系?
Mini-Transaction是包含的是⼀个DML操作对应的⼀组RedoLog,⽽⼀个事务中可能会包含多个DML操作,所以⼀个事务中包含⼀个或多个SQL语句,⼀个SQL语句包含⼀个或多个MRT,⼀个MTR包含⼀条或多条RedoLog,他们之间的关系如下图所⽰:
6.RedoLog是如何写⼊缓冲区的?
前置知识:这个问题可以理解为RedoLog的写⼊过程,要了解写⼊过程,必须先介绍RedoLog在内存和⽂件中是如何进⾏描述和组织的,我们提出以下⼏个问题:1.⽤来组织RedoLog的数据结构是什么?(1) ⽤来组织RedoLog的数据结构是Redo⻚,⻚的⼤⼩是 512B ,也可以称为⼀个 Redo LogBlock ,这个⼤⼩刚好对应磁盘上⼀个扇区,当⽇志写⼊磁盘时可以保证连续性, Redo LogBlock 的⽰意图如下所⽰:
(2)在⼀个 Redo Log Block 中,包含⽤来存储管理信息的块头 Log Block Header (占12Byte)
和块尾 Log Block Trailer (占4Byte),其他的空间是真正⽤来存储⽇志的区域 Log Block Body (占496B)2.Log Block Header和Log Block Trailer都记录了哪些信息?Log Block Header 和 Log Block Trailer 包含的信息如下图所⽰:
(1)Log Block Header1.LOG_BLOCK_HDR_NO :Block的唯⼀标识,是⼀个⼤于0的值,取值范围1~0x40000000UL,⽽0x40000000UL对应的整数是1073741824即1GB,也就是说InnoDB最多能够⽣成1GB个⽇志块,每个⽇志块为512B,所以InnoDB允许维护⽇志的最⼤容量为 512GB ,在后⾯介绍配置⽇志相关的选项时,关于⽇志容量的⼤⼩就是以此为依据;2.LOG_BLOCK_HDR_DATA_LEN :表⽰Block中已经使⽤了多少字节,由于块头占⽤了12B的空间,所以初始值为12,当 Log Block Body 被全部写满时那么这个值就是512;3.LOG_BLOCK_FIRST_REC_GROUP :如果⼀个MTR会⽣产多条redo⽇志记录,这些⽇志记录被称之为⼀个redo⽇志记录组, LOG_BLOCK_FIRST_REC_GROUP 代表该Block中第⼀个MTR中第⼀条⽇志的偏移量。4.LOG_BLOCK_CHECKPOINT_NO :表⽰检查点的编号,关于检查点后⾯会详细详解(2)Log Block TrailerLOG_BLOCK_CHECKSUM :表⽰Block的校验和,⽤于正确性校验。3.Redo Log Block在Log Buffer中是如何组织的?在内存中RedoLog存储在⽇志缓冲区(Log Buffer)中,⽇志缓冲区是服务器启动时向操作系统申请的⼀⽚连续的内存区域,并被划分成若⼲个连续的 Redo Log Block ,⽤来存储即将要写⼊磁盘⽇志⽂件的数据,如下图所⽰
⽇志缓冲区⼤⼩可以通过系统变量 innodb_log_buffer_size 指定,默认⼤⼩为 16MB ,取值范围1048576(1MB) ~ 4294967295(4GB)
# 查看当前Log Buffer的⼤⼩
mysql> show variables like 'innodb_log_buffer_size';
+------------------------+----------+
| Variable_name | Value |
+------------------------+----------+
| innodb_log_buffer_size | 16777216 |
+------------------------+----------+
1 row in set, 1 warning (0.05 sec)
# 设置Log Buffer的⼤⼩
mysql> set persist innodb_log_buffer_size =33554432;
Query OK, 0 rows affected (0.04 sec)
分析过程:
向⽇志缓冲区中写⼊⽇志是⼀个顺序写⼊的过程,也就是从缓冲区的第⼀个 Redo Log Block的 Log Block Body 开始依次向后写,⼀个block的空间空间⽤完之后再写下⼀个block,那么有⼀个⾸先要解决的问题,当有⼀记⽇志需要写⼊缓冲区时,应该往哪个block中的哪个位置写呢?1. 从⽇志缓冲区写RedoLog时从内存中的哪个地址开始写?InnoDB 的提供了⼀个名为 buf_free 的全局变量,该变量表⽰后续写⼊⽇志在 Log Buffer中的起始位置,如图所⽰:
2.不同的事务在并发执⾏时如何记录RedoLog?
(1)InnoDB以MTR为单位记录Redo Log,⼀个事务中包含多个MTR,⼀个MTR包含多条RedoLog,这些RedoLog是⼀个不可分割的⽇志组;(2)⼀个事务在执⾏过程中并不是每⽣成⼀条Redo Log就写⼊到Log Buffer中,⽽是把⽣成的RedoLog先缓存在内存的⼀个区域中,当⼀个MTR执⾏完成后把这组⽇志⼀起复制到Log Buffer;(3)假设有两个事务T1, T2并发执⾏,每个事务中都包含2个MRT,即事务T1包含mtr_t1_1和mtr_t1_2,T2包含mtr_t2_1和mtr_t2_2,如下图所⽰:
(4)在并发环境下不同事务中的MTR是交替执⾏的,当MTR执⾏完成之后对应⽣成的RedoLog会被写⼊Log Buffer,所以在Log Buffer中⽇志的写⼊形式如下图所⽰:
(5)需要说明⼀点,不同的MTR产⽣的⽇志组占⽤的存储空间可能不⼀样,有的MTR产⽣的⽇志很少,有的MTR产⽣的⽇志量⾮常多。解答问题1.RedoLog在内存中⽤Redo⻚进⾏组织,称为 Redo Log Block ,每个 Redo Log Block ⼤⼩固定为512B,对应磁盘上⼀个扇区,⽇志被顺序安排在 Log Block Body 中;2.在Log Buffer中多个 Redo Log Block 顺序排列, Redo Log Block 的个数由Log Buffer的⼤⼩决定;3.当执⾏事务时,不同的语句对应不同的数据库操作,⼀条SQL语句可能包含多个MTR,⼀个MTR包含多条RedoLog,MTR中的多条⽇志称为⼀个⽇志组,写⼊Log Buffer的⽇志是以MTR对应的⽇志组为⼀个单位,这组⽇志不可分割。
7.Redo Log的刷盘时机?
前置知识
Redo Log的落盘条件
1.当MTR中包含的日志组完整的保存在内存中
2.当事务提交之前,事务对应的日志必须要落盘
分析过程
当⼀个MTR执⾏完成后,RedoLog会被写⼊Log Buffer,⽽Log Buffer⼤⼩是有限的,并且这些记录⽇志的⽬的是为了服务器崩溃后的数据恢复,在内存中保存也不安全,所以把它们刷到磁盘上进⾏保存解答问题InnoDB在以下情况会把RedoLog刷到磁盘:(1)Log Buffer 空间不⾜时:Log Buffer⼤⼩是有限的,可以通过系统变量innodb_log_buffer_size 设置,如果当前Log Buffer中的RedoLog占⽤了Log Buffer总容量⼀半左右会触发刷盘;(2)事务提交时:当事务提交时,事务中对应的MTR已经完全记录在了Log Buffer中,在数据真正落盘之前,需要把对应的RedoLog刷新到磁盘;(3)后台线程定时刷盘:后台的 Master Thread 线程,⼤约每秒都会把Log Buffer中的RedoLog刷新到磁盘;(4)正常关闭服务器时:在服务关闭之前会把会把Log Buffer中的RedoLog刷新到磁盘;(5)做检查点( checkpoint )操作时:关于 checkpoint 后⾯会详细介绍衍⽣问题1. 刷盘策略可以进⾏配置吗? 可以(1)⽇志缓冲区的内容定期刷新到磁盘,可以通过系统变量 Innodb_flush_log_at_timeout=N设置,N默认为1,单位为秒;(2)通过设置系统变量 innodb_flush_log_at_trx_commit 设置写⼊和刷盘策略,默认值为10 :⽇志每秒写⼊系统缓冲区并刷新到磁盘,未写⼊系统缓冲区的事务⽇志可能会在MYSQL崩溃时丢失;1 :⽇志在每次事务提交时写⼊系统缓冲区并刷新到磁盘;2 :⽇志在每次事务提交后写⼊系统缓冲区并每秒⼀次刷新到磁盘,未刷新到磁盘的⽇志可能在系统崩溃时丢失。(3)如果启⽤⼆进制⽇志且设置 sync_binlog = 1 时,则必须设置 innodb_flush_log_at_trx_commit = 12. 不同的刷盘策略有什么影响?⾸先看⼀下Log Buffer、操作系统缓存和磁盘中⽇志⽂件的关系,如图所⽰:
这⾥主要讨论系统变量 innodb_flush_log_at_trx_commit 对应的⼏种情况:(1)值为0时:表⽰⽇志每秒写⼊操作系统缓存并刷新到磁盘,如果MySQL崩溃,那么在⼀秒内没有写⼊操作系统缓存的Redo Log将会丢失;(2)值为2时:⽇志在每次事务提交后写⼊系统缓冲区并每秒⼀次刷新到磁盘,此时已提交的事务Redo Log全部都写⼊了操作系统缓存,MySQL⽆论是否崩溃,Redo Log都会以指定的时间刷新到磁盘,但是如果服务器崩溃或断电,将会导致操作系统缓存中的Redo Log丢失;(3)值为 1 时:⽇志在每次事务提交时写⼊系统缓冲区并刷新到磁盘,此时Redo Log从Log Buffer中写⼊操作系统缓存并⽴即刷新到磁盘,从⽽尽可能的保证⽇志的完整性,推荐使⽤。
8.Redo Log对应磁盘上的⽂件是什么?
分析过程:
1.重做⽇志⽂件位于数据⽬录下的 #innodb_redo ⽬录中2.重做⽇志⽂件分为普通类型和备⽤类型,普通类型是正在使⽤的⽇志⽂件,备⽤是准备使⽤的⽇志⽂件,InnoDB 共维护 32 个重做⽇志⽂件,每个⽂件的⼤⼩等于 1/32 *innodb_redo_log_capacity3.重做⽇志⽂件使⽤ #ib_redo N 命名约定,其中 N 是重做⽇志⽂件编号,备⽤的重做⽇志⽂件使⽤ _tmp 为后缀。如下⽰例显⽰有21个活动(普通)重做⽇志⽂件和11个备⽤重做⽇志⽂件:
root@guangchen-vm:/var/lib/mysql/#innodb_redo# ls
'#ib_redo582' '#ib_redo590' '#ib_redo598' '#ib_redo606_tmp'
'#ib_redo583' '#ib_redo591' '#ib_redo599' '#ib_redo607_tmp'
'#ib_redo584' '#ib_redo592' '#ib_redo600' '#ib_redo608_tmp'
'#ib_redo585' '#ib_redo593' '#ib_redo601' '#ib_redo609_tmp'
'#ib_redo586' '#ib_redo594' '#ib_redo602' '#ib_redo610_tmp'
'#ib_redo587' '#ib_redo595' '#ib_redo603_tmp' '#ib_redo611_tmp'
'#ib_redo588' '#ib_redo596' '#ib_redo604_tmp' '#ib_redo612_tmp'
'#ib_redo589' '#ib_redo597' '#ib_redo605_tmp' '#ib_redo613_tmp'
4.每个普通的重做⽇志⽂件都与⼀个特定的 LSN 取值范围相关联,⽤于崩溃恢时快速定位到要执⾏重做的⽇志,可以使⽤下⾯的查询显⽰活动重做⽇志⽂件的 START_LSN 和 END_LSN 值;
mysql> SELECT FILE_NAME, START_LSN, END_LSN FROM
performance_schema.innodb_redo_log_files;
+----------------------------+--------------+--------------+
| FILE_NAME | START_LSN | END_LSN |
+----------------------------+--------------+--------------+
| ./#innodb_redo/#ib_redo582 | 117654982144 | 117658256896 |
| ./#innodb_redo/#ib_redo583 | 117658256896 | 117661531648 |
| ./#innodb_redo/#ib_redo584 | 117661531648 | 117664806400 |
| ./#innodb_redo/#ib_redo585 | 117664806400 | 117668081152 |
| ./#innodb_redo/#ib_redo586 | 117668081152 | 117671355904 |
| ./#innodb_redo/#ib_redo587 | 117671355904 | 117674630656 |
| ./#innodb_redo/#ib_redo588 | 117674630656 | 117677905408 |
| ./#innodb_redo/#ib_redo589 | 117677905408 | 117681180160 |
| ./#innodb_redo/#ib_redo590 | 117681180160 | 117684454912 |
| ./#innodb_redo/#ib_redo591 | 117684454912 | 117687729664 |
| ./#innodb_redo/#ib_redo592 | 117687729664 | 117691004416 |
解答问题:
1.重做⽇志⽂件位于数据⽬录下的 #innodb_redo ⽬录中,在MySQL8.0中InnoDB 共维护 32 个重做⽇志⽂件,每个⽂件的⼤⼩等于 1/32 * innodb_redo_log_capacity2.重做⽇志⽂件分为普通类型和备⽤类型,并且使⽤ #ib_redo N 命名约定,其中 N 是重做⽇志⽂件编号,备⽤的重做⽇志⽂件使⽤ _tmp 为后缀3.重做⽇志的总容量可以通过系统变量 innodb_redo_log_capacity 设置,8.0.34版本开始最⼤为512GB
衍⽣问题1.这么多⽇志⽂件⽇志写到哪个⽂件中?通过查看 #innodb_redo ⽬录,可以看到系统⽣成了32个RedoLog⽂件,当RedoLog从内存刷到磁盘时,先从第⼀个⽇志⽂件开始写,第⼀个写满之后顺序写到第⼆个,以此类推;如果最后⼀个也写满了,就会重新从第⼀个⽂件开始写,也就是说重做⽇志⽂件可以循环使⽤,如图所⽰:
如果循环写⼊的话,那么后写⼊的⽇志会不会把之前写⼊的内容覆盖了?当然有这个可能,为了解决这个问题,InnoDB提出checkpoint的概念2. 什么是LSN?(1) LSN是 Log Sequence Number 的简写,称为⽇志序号;(2)MySQL在运⾏期间,只要执⾏DML操作就会修改数据⻚,意味着会不断的⽣成RedoLog,InnoDB为了记录⽣成的⽇志总量(字节数),设计了⼀个只增不减的全局变量,这个全局变量就是LSN,起始值: 16*512 = 8192 ,最⼤值 2^64 - 1 ;(3)当⼀个MTR所包含的⼀组RedoLog被记录在 Redo Log Block 中时,实际是保存在 LogBlock Body 区域,但是在统计LSN增量时,如果MTR跨 Block 保存时,是按照实际写⼊的⽇志⼤⼩加上 Log Block Header 所占的12Byte)和块尾 Log Block Trailer 所占4Byte;(4)⽰例:a. 系统启动后初始化Log Buffer,buf_free指向第⼀个block偏移量为12Byte的位置,也就是block Header之后,此时LSN也会增加12,即8192 + 12 = 8204b. 假设MTR_1中包含的⼀组RedoLog⼤⼩为200Byte,那么LSN就会在原来的基础上加200,即:8204 + 200 = 8404c. 假设MTR_2中包含的⼀组RedoLog⼤⼩为1000Byte,这⾥当前的Block_1已经放不下这个MTR,于是⽇志顺序保存在后⾯的Block中,占满第⼆个Block后,直到使⽤了第三个Block的⼀部分空间,⽇志保存完成;这时LSN不但要记录MTR_2中⽇志的总⼤1000Byte,还要记录Block_1+Block_2的Log BlockTrailer和Block_2+Block_3的Log Block Header,总⼤⼩为:1000 + 12 * 2 + 4 * 2 = 1032,此时LSN的值为:8404 + 1032 = 9436,如下图所⽰:
9.Redo Log⽇志⽂件的格式?
分析过程:
1.Log Buffer中的Redo Log Block与磁盘中的Redo Log Block有哪些不同?
(1)在内存中Log Buffer是⼀⽚连续的内存空间,被划分成了若⼲个 512 字节⼤⼩的 Redo Log Block ⽤来保存Redo Log,将Log Buffer中的Redo Log刷新到磁盘,本质就是把 Redo Log Block 的写⼊⽇志⽂件中,所以Redo Log对应的⽇志⽂件其实也是由若⼲个 512 字节⼤⼩的 block 组成。MySQL会根据配置⽣成⼀组撤销⽇志⽂件,每个⽂件的格式和⼤⼩都⼀样,由两部分组成:管理区:前 2048 个字节,也就是前4个block存储⼀些⽇志⽂件的管理信息数据区:从第2048字节往后是⽤来存储Log Buffer对应的 Redo Log Block(2)也就是说真实的⽇志是从每个⽇志⽂件的第2048个字节开始写⼊,如图所⽰
(3)所以Log Buffer中的Redo Log Block与磁盘中的Redo Log Block在结构上是相同的,只不过在磁盘上多了⽤于⽂件管理的⽂件头信息
解答问题:磁盘中RedoLog的格式与内存中的格式相同,在内存中Log Buffer是⼀⽚连续的内存空间,被划分成了若⼲个 512 字节⼤⼩的 Redo Log Block ⽤来保存Redo Log,将Log Buffer中的Redo Log刷新到磁盘,本质就是把 Redo Log Block 的写⼊⽇志⽂件中,所以Redo Log对应的⽇志⽂件其实也是由若⼲个 512 字节⼤⼩的 block 组成,只不过在磁盘上多了⽤于⽂件管理的⽂件头信息衍⽣问题1. 重做⽇志⽂件管理区包含哪些信息?关于 Redo Log Block :的结构与内存结构相同,前 2048 字节 分为4个Block分别为:LOG_CHECKPOINT_1 :第⼀个⽇志⽂件中⽇志头的第⼀个检查点信息LOG_ENCRYPTION ⽇志⽂件头信息中的加密信息LOG_CHECKPOINT_2 :第⼀个⽇志⽂件中⽇志头的第⼆个检查点信息LOG_FILE_HDR_SIZE :⽇志⽂件头信息
2.管理区中具体管理了什么信息?
管理区各字段中的信息随着MySQL版本迭代变化⾮常⼤,这⾥主要介绍⼀些关键信息(1)LOG_CHECKPOINT_1 、 LOG_CHECKPOINT_2 :主要是记录CHECKPOINT操作时对应的LSN,LSN会交替写⼊到 LOG_CHECKPOINT_1 和 LOG_CHECKPOINT_2 中,具体写⼊规则后⾯介绍(2) LOG_ENCRYPTION : LOG_FILE_HDR_SIZE 中的加密信息,不做过多讨论(3)(LOG_FILE_HDR_SIZE :主要记录⽇志⽂件的⼀些信息,主要包括:LOG_HEADER_FORMAT :占4字节,⽇志的格式标识,和MySQL版本相关,有重⼤更新的版本才设置相应的值,在MySQL5.7.9之前⼀直都是0LOG_HEADER_START_LSN :占8字节,⽇志⽂件中第⼀个LSN编号和最后一个LSNLOG_HEADER_CREATOR :占32字节,记录⽇志的创建者,正常⽣成的⽇志⼀般 为"MEB"+MySQL的版本号,如果是运⾏ mysqlbackup 程序,在备份过程中⽣成的⽇志,则记录MySQL的版本号
10.什么是CHECKPOINT - 检查点?
分析过程:
RedoLog从内存刷到磁盘上的⽇志⽂件使⽤循环写⼊的⽅式,也就是从第⼀个⽇志⽂件顺序写到最后⼀个⽇志⽂件,当最后⼀个⽇志⽂件写满时⼜重新写第⼀个⽇志⽂件,那么就可能出现⽇志被覆盖的情况,那么哪些⽇志可以被覆盖哪些不能被覆盖呢?
1.哪些RedoLog可以被覆盖?
(1)⾸先回顾⼀下RedoLog的作⽤,RedoLog是⽤作崩溃后恢复没有完成落盘的事务,也就是说当Buffer Pool中的脏⻚写⼊RedoLog,但数据⻚还没有落盘时发⽣的崩溃,当服务器重启之后可以根据RedoLog进⾏恢复,这也是RedoLog的应⽤时机,所以这种状态下的RedoLog不能被覆盖,如下图所⽰:
(2)如果缓冲池中的脏⻚在记录RedoLog之后,也完成了真正的落盘操作,那么相应的RedoLog就没有⽤了,所以这部分RedoLog就可以被覆盖,如下图所⽰:
(3)经过分析可以看出,判断⽇志⽂件中的RedoLog是否可以覆盖的依据是它对应的数据⻚是否已经刷新到磁盘。
2.如何记录可以覆盖的⽇志⽂件位置?
(1)前⾯介绍过InnoDB使⽤LSN是来记录RedoLog总字节数,在这个基础上InnoDB采⽤⼀个全局变量 checkpoint_lsn 来记录当前系统中可以被覆盖⽇志总量是多少,也就是说checkpoint_lsn 记录已落盘脏⻚对应的⽇志结束时LSN的值,此时LSN⼩于checkpoint_lsn 的RedoLog就可以被覆盖,如图所⽰:
(2)当脏⻚刷新到磁盘之后,重新计算 checkpoint_lsn 的操作,称为⼀次 CHECKPOINT 操作,也可以说是重置⼀次检查点,系统会⽤⼀个 checkpoint_no 变量记录发⽣ CHECKPOINT 操作的次数,每做⼀ CHECKPOINT 操作 checkpoint_no 就会加1(3)由于RedoLog⽂件的⼤⼩是固定的,在系统启动时已经分配好了对应的 Redo Log Block ,所以很容易就可以根据 checkpoint_lsn 计算写⼊位置在⽇志⽂件中的偏移量
(4)关于检查点相关的 checkpoint_no 、 checkpoint_lsn 以及写⼊偏移量的信息会被记录在第⼀个⽇志⽂件的管理区,同时InnoDB规定,当checkpoint_no的值是偶数时写到checkpoint1 中,是奇数时写到 checkpoint2 中。解答问题:CHECKPOINT 也称为检查点,由于RedoLog⽂件是可以循环使⽤的,当最后⼀个⽂件写满时⼜会从第⼀个⽂件开始写⼊,这必将导致⽼的⽇志被覆盖, CHECKPOINT 是标记已被刷新到磁盘的脏⻚刷对应的RedoLog可以被覆盖的⼀种操作,当⽇志的LSN⼩于已落盘脏⻚对应的LSN都可以被覆盖。衍⽣问题1. 如果没有⼩于 checkpoint_lsn 的⽇志时如何处理?如果⽇志⽂件中没有⼩于 checkpoint_lsn 的⽇志时,表明⽇志⽂件已经使⽤完了,这时原来的⽇志不能被覆盖,InnoDB会先优先刷新脏⻚到磁盘,再做 CHECKPOINT 操作,之后再继续进⾏⽇志记录。
11.重做⽇志还有哪些主要的配置项?
分析过程:
1.重做⽇志在磁盘上所占的空间可以通过系统变量 innodb_redo_log_capacity 控制,变量值以字节为单位,最⼤值 549755813888 ,表⽰ 512GB ,可以在选项⽂件或在运⾏时使⽤ SET GLOBAL 语句进⾏设置,如下所⽰:
# 将RedoLog的最⼤容量设置为8GB
SET GLOBAL innodb_redo_log_capacity = 8589934592;
2.重做⽇志的⽬录可以通过系统变量 innodb_log_group_home_dir 进⾏设置,如果没有指定则⽇志⽂件位于数据⽬录的 #innodb_redo ⽬录中,如果定义了
innodb_log_group_home_dir 变量,则⽇志⽂件存放在该⽬录下的 #innodb_redo ⽬录中;
# 默认数据⽬录
root@guangchen-vm:/var/lib/mysql# ll
total 92948
# ... 省略
drwxr-x--- 2 mysql mysql 4096 11⽉ 5 11:15 '#innodb_redo'/ # 重做⽇志⽬录
# ... 省略
解答问题:根据实际应⽤场景通过配置对应的系统变量来指定 Redo Log 在磁盘上所占的空间的⼤⼩、所在⽬录等属性。衍⽣问题:1. 如何查看重做⽇志的状态?(1) 通过状态变量 innodb_redo_log_capacity_resized 显⽰当前重做⽇志容量限制:
(2) 可以通过查询 performance_schema.innodb_redo_log_files 表来查看活动重做⽇志⽂
件的信息
mysql> SELECT * FROM performance_schema.innodb_redo_log_files\G
*************************** 1. row ***************************FILE_ID: 6FILE_NAME: .\#innodb_redo\#ib_redo6START_LSN: 19656704END_LSN: 22931456SIZE_IN_BYTES: 3276800IS_FULL: 0
CONSUMER_LEVEL: 0
1 row in set (0.00 sec)
(3)通过使⽤ SHOW ENGINE InnoDB STATUS 访问 InnoDB 标准监视器输出中 LOG 部分查看有关Redo Log的信息
mysql> SHOW ENGINE INNODB STATUS\G
*************************** 1. row ***************************Type: InnoDBName:
Status:
=====================================
... # 省略
---
LOG
---
Log sequence number 21737291 # 当前的LSN
Log buffer assigned up to 21737291 # Log buffer中已分配的LSN
Log buffer completed up to 21737291 # Log buffer中已使⽤完成的LSN
Log written up to 21737291 # 已写⼊操作缓存的LSN
Log flushed up to 21737291 # 已刷新到⽇志⽂件的LSN
Added dirty pages up to 21737291 # 已添加的脏⻚对应的LSN
Pages flushed up to 21737291 # 最新添加到刷新链表⻚对应的LSN
Last checkpoint at 21737291 # 最后⼀次做checkpoint的LSN
Log minimum file id is 6 # ⽇志⽂件最⼩的编号
Log maximum file id is 6 # 普通⽇志⽂件的最⼤编号
24 log i/o's done, 0.00 log i/o's/second # 写⼊数据和速度
... # 省略
12.如何根据RedoLog进⾏崩溃恢复?
分析过程:在MySQL正常运⾏时,RedoLog不仅发挥不了它的作⽤⽽且还会对服务器的性能造成影响,但是服务器⼀旦崩溃,在重新启动时,就可以根据RedoLog中的记录把数据⻚恢复到崩溃前的状态1.如何确定哪些⽇志需要恢复?前⾯我们介绍过每⼀次 CHECKPOINT 操作都会重新计算 checkpoint_lsn , checkpoint_lsn 之前的⽇志表⽰已经被刷到磁盘数据⻚所⽣成的RedoLog,既然已被刷到磁盘,也就没有必要进⾏恢复,所以需要恢复的是 checkpoint_lsn 之后的⽇志2.如何获取最新的 checkpoint_lsn 和恢复的起点?RedoLog⽂件组中的第⼀个⽂件的管理信息中有两个block checkpoint1 和 checkpoint2 ,其中都存储了 checkpoint_lsn 和 checkpoint_no 信息,每次做 CHECKPOINT 操作时,会在这两个block中交替写⼊ CHECKPOINT 信息,只要需要把这两个block中保存的checkpoint_no 值⽐较⼀下,哪个值⼤就表⽰哪个block存储的就是最近的⼀次checkpoint信息。这样我们就能拿到最近发⽣的 checkpoint 对应的 checkpoint_lsn 值以及它在RedoLog⽂件组中的偏移量 checkpoint_offset3. 如何确认恢复的终点?我们⽤之前已经掌握的内容分析⼀下这个问题,⾸先RedoLog是顺序写⼊的,当⼀个block写满了之后再写下⼀个,⽽每⼀个block的 log block header 中都有⼀个名为LOG_BLOCK_HDR_DATA_LEN 的属性,该属性记录了当前block使⽤了多少字节,对于写满的block来说,该值⼀定是 512 ,所以找到第⼀个 LOG_BLOCK_HDR_DATA_LEN 的值不为512,就可以确定恢复扫描的最后⼀个block,这个block中的最后⼀条⽇志就是恢复的终点。4.如何进⾏恢复?(1)确定了需要扫描哪些⽇志进⾏崩溃恢复之后,接下来就是怎么进⾏恢复了,假设现在的⽇志⽂件中有RedoLog,如图所⽰
(2)第⼀条⽇志在 checkpoint_lsn 之前,表⽰已经落盘不⽤恢复;(3)checkpoint_lsn 之后的⽇志可以通过顺序扫描的⽅式,根据⽇志记录的内容依次恢复对应的数据⻚(4)InnoDB在顺序读取⽇志进⾏恢复的过程中采⽤了⼀些优化措施:⾸先根据⽇志的 Space Id 和Page No 计算出散列值,以这个散列值为 KEY ,把 Space Id 和 Page No 相同的⽇志放到哈希表的同⼀个槽⾥,如果有多个 Space Id 和 Page No 相同的⽇志,那么按照⽇志⽣成的先后顺序使⽤链表连接起来,如下图所⽰:
(5)组织好⽇志后,通过遍历哈希表,就可以⼀次把⼀个数据⻚中的修改全部恢复好,减少了读取数据⻚时的随机I/O次数5.如何确定哪些⽇志在崩溃前已经落盘?(1)checkpoint_lsn 之后的⽇志有可能就根本没有落盘,也有可能已经落盘但没有来的及做CHECKPOINT ,在恢复时如何区分呢?(2)在⻚结构章节介绍过,磁盘上的每个⻚都包含⼀个 File Header 信息,其中⼜包含已被刷到磁盘的LSN: FIL_PAGE_FILE_FLUSH_LSN 信息,在恢复时就可以通过当前⽇志对应的LSN与FIL_PAGE_FILE_FLUSH_LSN 进⾏⽐较,如果⽇志的LSN⼩于等于已刷新到磁盘的LSN,那就证明⽇志对应的数据在崩溃之前已经落盘,直接跳过即可解答问题恢复的过程主要分为以下⼏步:1. 通过 checkpoint_lsn 和第⼀个没有写满的⽇志⻚确定需要恢复⽇志的起始和结束位置;2. 遍历⽇志并把Space Id 和 Page No相同的⽇志组织在⼀起,以便⼀次性恢复完相应数据⻚的所有内容;3. ⽇志的LSN⼩于磁盘数据⻚⽂件记录的已刷新LSN时,表⽰这些数据在崩溃之前已落盘,跳过即可。
小结
1.RedoLog⽤于在数据库崩溃后恢复已提交事务还没有来的及落盘的数据,在保证事务的持久性和⼀致性⽅⾯起到了⾄关重要的作⽤2.RedoLog的写⼊时候,在UndoLog之后,脏⻚落盘之前3.RedoLog的由 Type 、 Space ID 、 Page no 和 data4.⽤来组织RedoLog的数据结构是Redo⻚,⻚的⼤⼩是 512B ,由 Log Block Header 、 Log Block Body 和 Log Block Trailer 组成,⽇志内容保存在 Log Block Body 中5.在Log Buffer和⽇志⽂件中Redo⻚的格式相同并且都是顺序排列的6.RedoLog在磁盘上所占的空间可以通过系统变量 innodb_redo_log_capacity 控制,变量值以字节为单位,最⼤ 512 GB