文章目录6. InnoDB 磁盘文件6.9 重做日志 - Redo Log6.9.5 什么是Mini-Transaction6.9.5.1 DML操作会对数据页产生什么样的影响6.9.5.2 在记录RedoLog时服务器崩溃了导致日志不完整怎么办6.9.5.3 Mini-Transaction的定义6.9.5.4 如何标识一组RedoLog属于同一个MTR6.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 不同的事务在并发执行时如何记录RedoLog6.9.7 Redo Log的刷盘时机6.9.7.1 刷盘策略可以进行配置吗6.9.7.2 不同的刷盘策略有什么影响6. InnoDB 磁盘文件6.9 重做日志 - Redo Log6.9.5 什么是Mini-Transaction6.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 HeaderLOG_BLOCK_HDR_NOBlock的唯一标识是一个大于0的值取值范围1~0x40000000UL而0x40000000UL对应的整数是1073741824即1GB也就是说InnoDB最多能够生成1GB个日志块每个日志块为512B所以InnoDB允许维护日志的最大容量为512GB在后面介绍配置日志相关的选项时关于日志容量的大小就是以此为依据LOG_BLOCK_HDR_DATA_LEN表示Block中已经使用了多少字节由于块头占用了12B的空间所以初始值为12当Log Block Body被全部写满时那么这个值就是512LOG_BLOCK_FIRST_REC_GROUP如果一个MTR会生产多条redo日志记录这些日志记录被称之为一个redo日志记录组LOG_BLOCK_FIRST_REC_GROUP代表该Block中第一个MTR中第一条日志的偏移量。LOG_BLOCK_CHECKPOINT_NO表示检查点的编号关于检查点后面会详细详解Log Block TrailerLOG_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_2T2包含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包含多条RedoLogMTR中的多条日志称为一个日志组写入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_timeoutN设置N默认为1单位为秒通过设置系统变量innodb_flush_log_at_trx_commit设置写入和刷盘策略默认值为10日志每秒写入系统缓冲区并刷新到磁盘未写入系统缓冲区的事务日志可能会在MYSQL崩溃时丢失秒为单位1日志在每次事务提交时写入系统缓冲区并刷新到磁盘事务为单位2日志在每次事务提交后写入系统缓冲区并每秒一次刷新到磁盘未刷新到磁盘的日志可能在系统崩溃时丢失。如果启用二进制日志且设置sync_binlog 1时则必须设置innodb_flush_log_at_trx_commit 16.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中写入操作系统缓存并立即刷新到磁盘从而尽可能的保证日志的完整性推荐使用。