【MySQL高阶】23.重做日志(1)
文章目录
- 6. InnoDB 磁盘文件
- 6.9 重做日志 - Redo Log
- 6.9.5 什么是Mini-Transaction?
- 6.9.5.1 DML操作会对数据页产生什么样的影响?
- 6.9.5.2 在记录RedoLog时服务器崩溃了导致日志不完整怎么办?
- 6.9.5.3 Mini-Transaction的定义
- 6.9.5.4 如何标识一组RedoLog属于同一个MTR?
- 6.9.5.5 如果一个MTR中只有一条日志是否可以优化?
- 6.9.5.6 事务与Mini-Transaction是什么关系?
- 6.9.6 RedoLog是如何写入缓冲区的?
- 6.9.6.1 用来组织RedoLog的数据结构是什么?
- 6.9.6.2 Log Block Header和Log Block Trailer都记录了哪些信息?
- 6.9.6.3 Redo Log Block在Log Buffer中是如何组织的?
- 6.9.6.4 从日志缓冲区写RedoLog时从内存中的哪个地址开始写?
- 6.9.6.5 不同的事务在并发执行时如何记录RedoLog?
- 6.9.7 Redo Log的刷盘时机?
- 6.9.7.1 刷盘策略可以进行配置吗?
- 6.9.7.2 不同的刷盘策略有什么影响?
6. InnoDB 磁盘文件
6.9 重做日志 - Redo Log
6.9.5 什么是Mini-Transaction?
6.9.5.1 DML操作会对数据页产生什么样的影响?
以一个
Insert操作为例,对数据页的影响一般分为两种情况:如果写入记录所在的数据页空间充足,足够存储一条将要写入的记录,那么就可以直接写入,如下图所示:
如果写入的数据页空间不充足,无法放下这条记录,由于在数据页中真实数据是按主键顺序排列的,那么就要新建一个数据页,对原来的数据进行调整,把一部分数据复制到新的数据页中,以便在目标数据页上留出足够的空间来保存即将写入的记录,此时对应的示意图如下所示:
因为新插入的数据按照主键顺序排列的,那么就要新建一个数据页,对原来的数据进行调整
通过以上两种情况下插入一条记录的分析可以看出,当数据页空间充足的情况下可以直接写入数据,并记录一条对应
RedoLog即可当数据页空间不充足无法放下这条记录的情况下,会创建一个新数据页,同时还有数据的复制和写入,索引树非叶子节点上修改,在实际的执行过程中还有对表空间中段、区中统计信息的修改等等,这意味一个简单的
Insert操作有会产生很多条RedoLog。
6.9.5.2 在记录RedoLog时服务器崩溃了导致日志不完整怎么办?
那么这时有一个问题需要考虑,试想一下如果执行这一系统操作的时候,
RedoLog只记录了一半(不完整的日志)服务器就崩溃了,那么当服务器重启的时候如果按照RedoLog进行恢复,得到的结果肯定是错误的。所以在记录
RedoLog的时候要保证一个DML所对应的一系列日志必须是完整的才可以执行恢复操作,否则就不执行恢复。那么怎么才能标记
DML操作对应的日志是完整的呢?
6.9.5.3 Mini-Transaction的定义
Mini-Transaction就是针对以上的操作过程定义的概念,也就是说把记录一个DML操作的过程称为一个Mini-Transaction,简称MTR,一个所谓的MTR包含一个DML操作产生的一组完整日志,在进行崩溃恢复时这一组RedoLog做为一个不可分割的整体(要么全执行,要么全不执行)。- 这里所说的不可分割的组是
MySQL中定义的,常见的有:
- 向聚簇索引对应
B+树的页面中插入一条记录时产生的RedoLog不可分割;- 向某个二级索引对应
B+树的页面中插入一条记录时产生的RedoLog不可分割;- 还有其他的一些对页面的访问操作时产生的
RedoLog不可分割。- 每条语句根据具体的执行情况可能会产生多个
MTR。
总结:
Mini-Transaction是MySQL内部对底层数据页的一个原子操作,包含一个DML操作产生的一组完整日志,保证数据库异常恢复时数据页中数据的一致性。
6.9.5.4 如何标识一组RedoLog属于同一个MTR?
在执行
DML操作的过程中,每一个对数据页的修改都会记录一条RedoLog,这些日志会被顺序记录下来,并在这组日志的最后加一条特殊的日志标识作为一个MRT的结尾。这条特殊的日志结构非常简单,只有一个
TYPE字段,类型为MLOG_MULTI_REC_END = 31,也就是日志分类中的提供额外信息的日志类型,一个MTR对应的日志组,如下图所示:
6.9.5.5 如果一个MTR中只有一条日志是否可以优化?
- 当然可以,如果一个
MTR只有一条日志,直接在这条日志后加一个类型为MLOG_MULTI_REC_END = 31的标识可以做为MTR的结尾,但这样做有点浪费空间;
InnoDB为了尽可能的节省空间,在MTR只有一条日志的情况下,做了一个优化。通过上面的介绍了解了日志类型虽然很多,但也只有几十种,而用来表示日志类型的TYPE字段长度为1BTYE, 而这1BTYE中只用7个比特位,代表整数127,就完全可以表示所有的日志类型,与是省出来一个比特位就可以用来表示当前MTR包含一条还是一组RedoLog,也就是说如果TYPE字段的第一个比特位为1,表示MTR只包含一条RedoLog,为0表示MTR包含一组RedoLog,如下图所示:
6.9.5.6 事务与Mini-Transaction是什么关系?
Mini-Transaction是包含的是一个DML操作对应的一组RedoLog,而一个事务中可能会包含多个DML操作,所以一个事务中包含一个或多个SQL语句,一个SQL语句包含一个或多个MTR,一个MTR包含一条或多条RedoLog,他们之间的关系如下图所示:
6.9.6 RedoLog是如何写入缓冲区的?
6.9.6.1 用来组织RedoLog的数据结构是什么?
用来组织
RedoLog的数据结构是Redo页,页的大小是512B,也可以称为一个Redo Log Block,这个大小刚好对应磁盘上一个扇区,当日志写入磁盘时可以保证连续性,Redo Log Block的示意图如下所示:
在一个
Redo Log Block中,包含用来存储管理信息的块头Log Block Header(占12Byte)和块尾Log Block Trailer(占4Byte),其他的空间是真正用来存储日志的区域Log Block Body(占496B)
6.9.6.2 Log Block Header和Log Block Trailer都记录了哪些信息?
Log Block Header和Log Block Trailer包含的信息如下图所示:
Log Block Header
LOG_BLOCK_HDR_NO:Block的唯一标识,是一个大于0的值,取值范围1~0x40000000UL,而0x40000000UL对应的整数是1073741824即1GB,也就是说InnoDB最多能够生成1GB个日志块,每个日志块为512B,所以InnoDB允许维护日志的最大容量为512GB,在后面介绍配置日志相关的选项时,关于日志容量的大小就是以此为依据;LOG_BLOCK_HDR_DATA_LEN:表示Block中已经使用了多少字节,由于块头占用了12B的空间,所以初始值为12,当Log Block Body被全部写满时那么这个值就是512;LOG_BLOCK_FIRST_REC_GROUP:如果一个MTR会生产多条redo日志记录,这些日志记录被称之为一个redo日志记录组,LOG_BLOCK_FIRST_REC_GROUP代表该Block中第一个MTR中第一条日志的偏移量。LOG_BLOCK_CHECKPOINT_NO:表示检查点的编号,关于检查点后面会详细详解
Log Block Trailer
LOG_BLOCK_CHECKSUM:表示Block的校验和,用于正确性校验。
6.9.6.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 (0.00 sec) # 设置Log Buffer的大小 mysql> set persist innodb_log_buffer_size =33554432; Query OK, 0 rows affected (0.03 sec) mysql>向日志缓冲区中写入日志是一个顺序写入的过程,也就是从缓冲区的第一个
Redo Log Block的Log Block Body开始依次向后写,一个block的空间用完之后在写下一个block。那么有一个首先要解决的问题,当有一个日志需要写入缓冲区的时候,应该往哪个
block中的位置写呢?
6.9.6.4 从日志缓冲区写RedoLog时从内存中的哪个地址开始写?
InnoDB的提供了一个名为buf_free的全局变量,该变量表示后续写入日志在Log Buffer中的起始位置。如图所示:
6.9.6.5 不同的事务在并发执行时如何记录RedoLog?
- 通过前面的介绍了解到,
InnoDB以MTR为单位记录RedoLog,一个事务中包含多个MTR,一个MTR包含多条RedoLog,这些RedoLog是一个不可分割的日志组;- 一个事务在执行过程中并不是每生成一条
RedoLog就写入到Log Buffer中,而是把生成的RedoLog先缓存在内存的一个区域中,当一个MTR执行完成后把这组日志一起复制到Log Buffer;- 假设有两个事务
T1, T2并发执行,每个事务中都包含2个MRT,即事务T1包含mtr_t1_1和mtr_t1_2,T2包含mtr_t2_1和mtr_t2_2,如下图所示:
- 在并发环境下不同事务中的
MTR是交替执行的,当MTR执行完成之后对应生成的RedoLog会被写入Log Buffer,所以在Log Buffer中日志的写入形式如下图所示:
- 需要说明一点,不同的
MTR产生的日志组占用的存储空间可能不一样,有的MTR产生的日志很少,有的MTR产生的日志量非常多。
总结:
RedoLog在内存中用Redo页进行组织,称为Redo Log Block,每个Redo Log Block大小固定为512B,对应磁盘上一个扇区,日志被顺序安排在Log Block Body中;- 在
Log Buffer中多个Redo Log Block顺序排列,Redo Log Block的个数由Log Buffer的大小决定;- 当执行事务时,不同的语句对应不同的数据库操作,一条
SQL语句可能包含多个MTR,一个MTR包含多条RedoLog,MTR中的多条日志称为一个日志组,写入Log Buffer的日志是以MTR对应的日志组为一个单位,这组日志不可分割。
6.9.7 Redo Log的刷盘时机?
当一个
MTR执行完成后,RedoLog会被写入Log Buffer,而Log Buffer大小是有限的,并且这些记录日志的目的是为了服务器崩溃后的数据恢复,在内存中保存也不安全,所以在把它们刷到磁盘上进行保存
总结:
InnoDB在以下情况会把RedoLog刷到磁盘:
Log Buffer空间不足时:Log Buffer大小是有限的,可以通过系统变量innodb_log_buffer_size设置,如果当前Log Buffer中的RedoLog占用了Log Buffer总容量一半左右会触发刷盘;- 事务提交前,事务对应的日志必须落盘
- 当事务提交时,事务中对应的
MTR已经完整的记录在了Log Buffer中,在数据真正落盘之前,需要把对应的RedoLog刷新到磁盘;- 后台线程定时刷盘:后台的
Master Thread线程,大约每秒都会把Log Buffer中的RedoLog刷新到磁盘;- 正常关闭服务器时:在服务关闭之前会把会把
Log Buffer中的RedoLog刷新到磁盘;- 做检查点(
checkpoint)操作时:关于checkpoint后面会详细介绍
6.9.7.1 刷盘策略可以进行配置吗?
- 可以
- 日志缓冲区的内容定期刷新到磁盘,可以通过系统变量
Innodb_flush_log_at_timeout=N设置,N默认为1,单位为秒;- 通过设置系统变量
innodb_flush_log_at_trx_commit设置写入和刷盘策略,默认值为1
0:日志每秒写入系统缓冲区并刷新到磁盘,未写入系统缓冲区的事务日志可能会在MYSQL崩溃时丢失;(秒为单位)1:日志在每次事务提交时写入系统缓冲区并刷新到磁盘;(事务为单位)2:日志在每次事务提交后写入系统缓冲区并每秒一次刷新到磁盘,未刷新到磁盘的日志可能在系统崩溃时丢失。- 如果启用二进制日志且设置
sync_binlog = 1时,则必须设置innodb_flush_log_at_trx_commit = 1
6.9.7.2 不同的刷盘策略有什么影响?
首先看一下
Log Buffer、操作系统缓存和磁盘中日志文件的关系,如图所示:
这里主要讨论系统变量
innodb_flush_log_at_trx_commit对应的几种情况:
- 值为
0时:表示日志每秒写入操作系统缓存并刷新到磁盘,如果MySQL崩溃,那么在一秒内没有写入操作系统缓存的Redo Log将会丢失;- 值为
2时:日志在每次事务提交后写入系统缓冲区并每秒一次刷新到磁盘,此时已提交的事务Redo Log全部都写入了操作系统缓存,MySQL无论是否崩溃,Redo Log都会以指定的时间刷新到磁盘,但是如果服务器崩溃或断电,将会导致操作系统缓存中的Redo Log丢失;- 值为
1时:日志在每次事务提交时写入系统缓冲区并刷新到磁盘,此时Redo Log从Log Buffer中写入操作系统缓存并立即刷新到磁盘,从而尽可能的保证日志的完整性,推荐使用。
