MySQL 的 redo 日志(重做日志)是 InnoDB 存储引擎用于保证事务持久性(即 Durability,即 "D" 在 ACID 中的作用)的一种机制。Redo 日志记录了对数据库的所有修改操作,它确保在系统崩溃后可以通过重新执行这些日志中的操作来恢复数据。对于 redo 日志的刷盘时机,InnoDB 采用了以下几种策略来决定何时将 redo 日志写入磁盘:
1. 事务提交时刷盘(Commit 时)
- 刷盘时机:当一个事务提交时,InnoDB 会将该事务对应的 redo 日志从内存中的日志缓冲区(log buffer)刷写到磁盘中的 redo 日志文件(通常是
ib_logfile0
和 ib_logfile1
)。
- 原理:事务的提交是通过 两阶段提交 协议来保证的。首先,InnoDB 会先将事务的所有修改操作(包括 redo 日志)写入磁盘,之后才会将提交的信号发给应用程序。如果发生崩溃,已经写入磁盘的 redo 日志将会被重新执行,保证数据的持久性。
2. log buffer 刷盘(定期刷新)
- 刷盘时机:InnoDB 会定期将内存中的日志缓冲区中的数据刷写到磁盘。这是为了防止 redo 日志缓冲区过大,防止内存溢出或丢失数据。
- 触发条件:
- log_flush_interval:这是一个系统变量,表示日志缓冲区刷盘的最大时间间隔。例如,
innodb_flush_log_at_trx_commit
为 1 时,表示每次事务提交都会刷新日志缓冲区;当值为 2 或 0 时,InnoDB 会定期刷新日志缓冲区。
- log buffer 满:如果日志缓冲区满了,InnoDB 会将它刷写到磁盘。
- 系统负载:当系统的负载较轻时,日志可能会更频繁地刷写。
3. 根据 innodb_flush_log_at_trx_commit
刷盘
- 参数控制:
innodb_flush_log_at_trx_commit
是一个非常重要的参数,用于控制 redo 日志的刷新行为。它的值决定了事务提交时如何刷盘。这个参数有以下几种设置:
1
:每次事务提交时,都会将 redo 日志从内存刷写到磁盘,并且会强制磁盘写入操作。这是最安全的设置,保证每次事务提交都会持久化到磁盘,但性能较低。
2
:每次事务提交时,日志会写入操作系统的缓冲区,但不一定立即刷盘到磁盘。这是为了提高性能,但可能在系统崩溃时丢失最后一秒的数据。
0
:事务提交时,日志不会立即刷写到磁盘,而是由操作系统定期刷新。这种方式提供了最高的性能,但也有较大的数据丢失风险。
4. 检查点(Checkpoint)时刷盘
- 刷盘时机:InnoDB 会定期进行检查点操作,也就是将 redo 日志刷写到磁盘中的过程。检查点是一个数据库的内部机制,用来确保 redo 日志文件不会无限增长,并且防止大量的 redo 日志积压。每次检查点的执行,都会把所有已经提交的事务的 redo 日志刷新到磁盘。
- 原理:每个检查点的时间间隔是由
innodb_max_dirty_pages_pct
参数控制的,该参数限制了数据页的脏页比例。脏页的数量过多时,系统会触发一个检查点,将这些脏页写回磁盘并刷新 redo 日志。
5. 磁盘刷盘的条件
- 写入磁盘时机:除了事务提交时的刷新外,InnoDB 还可能在系统空闲时将 redo 日志缓冲区中的数据刷写到磁盘。具体时机由操作系统的磁盘调度策略和 InnoDB 的配置参数共同决定。
6. 日志组和日志文件大小
- 日志组和日志文件:MySQL 通过多个 redo 日志文件来组织 redo 日志。默认情况下,InnoDB 使用两个日志文件(
ib_logfile0
和 ib_logfile1
)。这些日志文件通常都较大,以减少频繁的磁盘写操作,从而提高性能。
- 刷新到磁盘的方式:每次写入操作都会追加到当前的日志文件,日志文件填满后会切换到下一个文件。日志切换时,日志缓冲区也会被刷新到磁盘。