15.6.5-Redo日志

Redo log是存储在磁盘上的一种数据结构,在崩溃恢复期间用来纠正由不完整的事务产生的数据。在正常操作中,redo log会对由底层api或sql语句产生的数据变更请求进行编码。意外关闭前未完成数据文件更新的修改操作会在初始化时,接受请求之前进行重放。想了解redo log在崩溃恢复过程中的角色,请查阅章节 15.18.2,“InnoDB Recovery”。

redo log在磁盘上的代表形式是redo log文件。写入redo log文件的数据以受影响的记录进行编码,这些数据被统称为重做。数据在数据文件中的流逝,是用一个不断增长的LSN值表示的。在发生数据修改时,会追加redo log。在checkpoint处理程序的运行,最早的数据会被截断。

下面的章节中,将会介绍redo log相关的信息和过程:

  • 设置Redo Log容量(MySQL 8.0.30或更高)
  • 设置Redo Log容量(MySQL 8.0.30之前)
  • 重做日志容量自动设置
  • 重做日志归档
  • 禁用重做日志
  • 相关话题

设置Redo Log容量(MySQL 8.0.30或更高) #

从MySQL 8.0.30开始,系统变量innodb_redo_log_capacity控制着redo log文件占用的磁盘空间。你可以在启动时在配置文件中配置,或在运行时通过set global语句来设置;例如,下面的语句将redo log容量改为8G:

set global innodb_redo_log_capacity=8G;

如果是在运行时设置,设置修改会立即生效,但新的限制完全实施可能需要花费一些时间。如果redo日志文件占用的空间比限制值小,从buffer pool刷脏页的表空间文件的操作就会不那么积极。如果redo日志文件占用的空间比限制值大,刷脏页就会更积极,最终会降低重做日志文件占用的空间。

变量innodb_redo_log_capacity会取代innodb_log_files_in_group和innodb_log_file_size这两个变量。如果设置了innodb_redo_log_capacity,这两个变量就会被忽略。否则,这两个设置就会被用来计算innodb_redo_log_capacity的值(innodb_log_files_in_group * innodb_log_file_size)。如果所有的变量都没有设置,redo log空间容量会被设置成innodb_redo_log_capacity的默认值,这个默认值是100MB。redo log的最大空间容量是128GB。

重做日志文件位于数据目录下的#innodb_redo目录内,除非通过变量innodb_log_group_home_dir设置了一个不同的目录。如果设置了innodb_log_group_home_dir,redo log文件就会创建在这个目录下的#innodb_redo子目录内。有两种类型的redo日志文件:普通的和空闲的。 普通日志文件是被使用的文件。空闲日志文件是即将被使用的。InnoDB会维护共32个redo日志文件,每个文件的大小是1/32 * innodb_redo_log_capacity;然而,在修改了innodb_redo_log_capacity的值后,过一段时间或,这个值会改变。

Redo日志文件的命名规则是#ib_redoN,其中N是redo日志文件编号。空闲redo日志文件会带_tmp后缀。下面的例子展示了一个#innodb_redo目录下的redo日志文件,共有21个活跃的日志文件,11个空闲的日志文件,文件编号顺序排列:

'#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'

每一个普通redo日志文件都与一个特殊的LSN区间相关联;例如,下面的查询展示了前面例子中的活跃redo日志文件的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 |
| ./#innodb_redo/#ib_redo593 | 117691004416 | 117694279168 |
| ./#innodb_redo/#ib_redo594 | 117694279168 | 117697553920 |
| ./#innodb_redo/#ib_redo595 | 117697553920 | 117700828672 |
| ./#innodb_redo/#ib_redo596 | 117700828672 | 117704103424 |
| ./#innodb_redo/#ib_redo597 | 117704103424 | 117707378176 |
| ./#innodb_redo/#ib_redo598 | 117707378176 | 117710652928 |
| ./#innodb_redo/#ib_redo599 | 117710652928 | 117713927680 |
| ./#innodb_redo/#ib_redo600 | 117713927680 | 117717202432 |
| ./#innodb_redo/#ib_redo601 | 117717202432 | 117720477184 |
| ./#innodb_redo/#ib_redo602 | 117720477184 | 117723751936 |
+----------------------------+--------------+--------------+

在进行一个checkpoint操作时,InnoDB会把checkpoint LSN,存储在包含LSN的文件的header中。在恢复过程中,会检查所有的redo文件,会从最新的checkpoint开始恢复。

一些变量被用来监控redo日志和redo日志文件的容量变化操作;例如,你可以通过查询 Innodb_redo_log_resize_status来查询一个容量变化操作的状态:

mysql> SHOW STATUS LIKE 'Innodb_redo_log_resize_status';
+-------------------------------+-------+
| Variable_name | Value |
+-------------------------------+-------+
| Innodb_redo_log_resize_status | OK |
+-------------------------------+-------+

状态变量Innodb_redo_log_capacity_resized显示了当前redo日志文件容量限制:

mysql> SHOW STATUS LIKE 'Innodb_redo_log_capacity_resized';
 +----------------------------------+-----------+
| Variable_name | Value |
+----------------------------------+-----------+
| Innodb_redo_log_capacity_resized | 104857600 |
+----------------------------------+-----------+

其它一些状态变量包括:

  • Innodb_redo_log_checkpoint_lsn
  • Innodb_redo_log_current_lsn
  • Innodb_redo_log_flushed_to_disk_lsn
  • Innodb_redo_log_logical_size
  • Innodb_redo_log_physical_size
  • Innodb_redo_log_read_only
  • Innodb_redo_log_uuid 想了解更多信息,请查阅状态变量相关描述。

你可以查询Performance Schema的innodb_redo_log_files表来查看活跃redo日志文件的信息。下面的查询从这张表的所有列获取数据:

SELECT FILE_ID, START_LSN, END_LSN, SIZE_IN_BYTES, IS_FULL, CONSUMER_LEVEL 
FROM performance_schema.innodb_redo_log_files;

设置Redo Log容量(MySQL 8.0.30之前) #

在MySQL 8.0.30之前,InnoDB默认在数据目录下创建两个redo日志文件,分别是ib_logfile0和ib_logfile1,会以循环的方式写入这些文件。

修改redo日志文件需要修改redo日志文件的数量或大小,或两者都修改。

  1. 停止MySQL服务器,确保停止过程中没有发生错误。
  2. 修改my.cnf,改变redo文件设置。要改变redo日志文件大小,设置innodb_log_file_size。要增加redo日志文件数量,设置innodb_log_files_in_group.。
  3. 启动MySQL服务器。

如果InnoDB检测到innodb_log_file_size和redo日志文件大小不同,它会写入日志checkpoint,关闭并移除旧的日志文件,按照新的要求创建文件,并打开新的文件。

重做日志容量自动设置 #

如果启用了innodb_dedicated_server,InnoDB会自动设置一些InnoDB参数,包括redo日志容量。自动设置适用于MySQL独享实例,这种情形下MySQL服务器可以使用所有系统资源。想了解更多信息,请查阅章节 15.8.12,“Enabling Automatic Configuration for a Dedicated MySQL Server”.

重做日志归档 #

备份工具在备份过程中,复制redo日志记录的速度可能会跟不上redo日志的生成速度,导致丢失redo日志记录,因为这些记录会被覆盖。这个问题经常发生在备份过程中MySQL压力较大且redo日志文件存储介质速度比备份介质速度更快。MySQL 8.0.17中引入的redo日志归档功能,通过在写入redo日志文件时将redo日志记录顺序写入归档文件解决了这个问题。备份工具可以在必要时从归档文件中复制redo记录,从而避免了可能得数据丢失。

如果在服务器上设置了redo日志归档,MySQL Enterprise Backup(企业版MySQL可用),会在MySQL备份时使用redo日志归档。

启用redo日志归档需要设置系统变量innodb_redo_log_archive_dirs。其值是一个使用分隔符分割的redo日志归档目录列表。label:directory对被:分隔。例如:

mysql> SET GLOBAL innodb_redo_log_archive_dirs='label1:directory_path1[;label2:directory_path2;…]';

label是归档目录的一个标识符。它可以是任意字母组成的字符串(不含:)。允许空标签,但是即使使用空标签,也需要带:符号。 directory_path是必填项。当redo日志文件归档被激活时,选择的redo日志归档目录必须存在,否则就会返回一个错误。这个路径可以包含符号:,但是不能包含;。

在激活redo日志归档前,必须设置变量innodb_redo_log_archive_dirs。默认值是NULL,此时不允许激活redo日志归档。

指定的redo日志归档目录必须符合以下要求:

  • 目录必须存在。redo日志归档进程不会创建目录。否则,会返回下面的错误:
ERROR 3844 (HY000): Redo log archive directory
'directory_path1' does not exist or is not a directory
  • 目录一定不能是所有用户都可以访问。这是为了避免将redo日志数据暴露给未授权的用户。否则,会返回下面的错误:
ERROR 3846 (HY000): Redo log archive directory
'directory_path1' is accessible to all OS users
  • 目录不能是以下参数定义的:datadir, innodb_data_home_dir, innodb_directories, innodb_log_group_home_dir, innodb_temp_tablespaces_dir, innodb_tmpdir innodb_undo_directory, 或 secure_file_priv。也不能是这些目录的父目录下或子目录下。否则,会返回一个和下面的错误类似的错误:
ERROR 3845 (HY000): Redo log archive directory
'directory_path1' is in, under, or over server directory
'datadir' - '/path/to/data_directory'

当一个支持redo日志归档的备份工具进行备份时,备份工具通过调用函数innodb_redo_log_archive_start()来启动redo日志归档。

如果你没有使用支持redo日志归档的备份工具,也可以通过手动方式启动归档,如下:

mysql> SELECT innodb_redo_log_archive_start('label', 'subdir');
+------------------------------------------+
| innodb_redo_log_archive_start('label') |
+------------------------------------------+
| 0 |
+------------------------------------------+

或:

mysql> DO innodb_redo_log_archive_start('label', 'subdir');
Query OK, 0 rows affected (0.09 sec)

激活redo日志归档的会话(使用innodb_redo_log_archive_start())必须在归档期间保持连接。比如在同一个会话中停止redo日志归档(使用innodb_redo_log_archive_stop())。如果这个会话在redo日志归档停止之前被关闭,服务器会停止归档,删除归档文件。
其中label是一个 innodb_redo_log_archive_dirs定义的标签;subdir是可选参数,用来指定label指定的目录的子目录。这必须是一个简单目录(不能有/,,;)。subdir可以是空,null,也可以省略。

只有拥有INNODB_REDO_LOG_ARCHIVE权限的用户才能通过调用 innodb_redo_log_archive_start()激活redo日志归档,或使用innodb_redo_log_archive_stop()来停止归档。运行备份工具或启动/停止redo日志归档的用户,必须有这个权限。

redo日志归档文件路径是 directory_identified_by_label/ [subdir/]archive.serverUUID.000001.log,其中 directory_identified_by_label是label参数指定的归档目录,sudir是可选参数。

例如,一个redo日志归档文件的完整路径类似于:

/directory_path/subdirectory/archive.e71a47dc-61f8-11e9-a3cb-080027154b4d.000001.log

在备份工具复制完InnoDB数据文件后,它会调用 innodb_redo_log_archive_stop()函数来停止redo日志归档。

如果你没有使用支持redo日志归档的备份工具,也可以手工停止日志归档,如下:

mysql> SELECT innodb_redo_log_archive_stop();
+--------------------------------+
| innodb_redo_log_archive_stop() |
+--------------------------------+
| 0 |
+--------------------------------+

或:

mysql> DO innodb_redo_log_archive_stop();
Query OK, 0 rows affected (0.01 sec)

在停止函数执行结束后,备份工具会从归档文件中查找redo日志数据的相关段,将其复制到备份数据中。

在备份工具复制完redo日志数据后,不再需要redo日志归档文件后,它会删除归档文件。

通常,删除归档日志文件是备份工具的事。然而,如果归档操作在调用 innodb_redo_log_archive_stop()之前退出,MySQL服务器会移除这个文件。

性能考量 #

因为会带来额外的写操作,启动redo日志归档通常会带来一些性能成本。

在Unix和类Unix系统上,性能影响通常很小,前提是没有持续的高速率更新。在Windows上,成本通常会更高一些。

如果有持续的高速率更新,且redo日志归档文件所在存储介质性能较差,影响大小就不一定了。

写入redo日志归档文件通常不会影响正常的事务日志,除非归档日志文件所在的存储介质的速度比redo日志文件所在的存储介质差很多,且有大量的持久化重做日志块等待被归档。这种时候,事务日志的速率就会降低,受归档日志文件所在存储介质的速度限制。

禁用重做日志 #

从MySQL8.0.21开始,你可以通过 ALTER INSTANCE DISABLE INNODB REDO_LOG语句禁用redo日志。这个功能主要用于新MySQL实例加载数据。禁用redo日志避免了redo日志写入和doublewrite buffering,会提高加载数据的速度。

这个特性只用于新实例加载数据。不要在生产环境中禁用redo日志。在禁用redo日志后,允许关闭和重启服务器。但是禁用redo日志后,发生意外关闭时,可能会导致数据丢失和示例损坏。

禁用redo日志时,在数据库服务意外停止后,尝试重启服务会被拒绝,并报下面的错误:

[ERROR] [MY-013598] [InnoDB] Server was killed when Innodb Redo 
logging was disabled. Data files could be corrupt. You can try 
to restart the database with innodb_force_recovery=6

在这种情况下,初始化一个新的MySQL实例,然后重新启动数据加载程序。

禁用和开启redo日志,需要INNODB_REDO_LOG_ENABLE权限。

可以通过变量 Innodb_redo_log_enabled 来查看redo日志状态。

禁用redo日志时不允许进行克隆操作和redo日志归档,反之亦然。

ALTER INSTANCE [ENABLE|DISABLE] INNODB REDO_LOG操作需要一个排它的备份元数据锁,这个锁会阻止其它并发的 ALTER INSTANCE操作。 其它并发的 ALTER INSTANCE操作需要等待锁被释放。

下面的过程展示了怎样禁用redo日志,然后加载数据到新的MySQL实例。

  1. 在新的MySQL实例上,赋予禁用redo日志的用户INNODB_REDO_LOG_ENABLE权限。
mysql> GRANT INNODB_REDO_LOG_ENABLE ON *.* to 'data_load_admin';
  1. 使用data_load_admin 用户,禁用redo日志:
mysql> ALTER INSTANCE DISABLE INNODB REDO_LOG;
  1. 检查 the Innodb_redo_log_enabled状态,确保redo日志已被禁用:
+-------------------------+-------+
| Variable_name | Value |
+-------------------------+-------+
| Innodb_redo_log_enabled | OFF |
+-------------------------+-------+
  1. 执行数据加载操作。

  2. 使用 data_load_admin用户,在数据加载操作完成后启动redo日志:

mysql> ALTER INSTANCE ENABLE INNODB REDO_LOG;
  1. 检查 Innodb_redo_log_enabled状态,确保redo日志已被启用:
mysql> SHOW GLOBAL STATUS LIKE 'Innodb_redo_log_enabled';
+-------------------------+-------+
| Variable_name | Value |
+-------------------------+-------+
| Innodb_redo_log_enabled | ON |
+-------------------------+-------+

相关话题 #

  • Redo 日志设置
  • 章节8.5.4,“优化InnoDB日志”
  • Redo日志加密