MySQL InnoDB 错误:”log sequence number is in the future”

适用于:

MySQL 服务器4.0及以上

本文信息适用于所有平台

 

症状

当尝试在恢复后使用InnoDB,会发生以下错误。

 

 

ERROR
‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
120207 13:30:29 InnoDB: Error: page 573441 log sequence number 22 697197707
InnoDB: is in the future! Current system log sequence number 5 2916730276.
InnoDB: Your database may be corrupt or you may have copied the InnoDB
InnoDB: tablespace but not the InnoDB log files. See
InnoDB: http://dev.mysql.com/doc/refman/5.5/en/forci ng‐recovery.html
InnoDB: for more information.

 

更改

这个问题通常发生在以下更改之后:

  • 如果InnoDB日志文件被删除且这些事件其中之一或更多发生在删除之前(没有任何一个事件是自己发生的—它由以下事件之一以及删除日志文件两个条件而引起):
    • 硬件服务器崩溃
    • Mysqld进程由于kill ‐9或其他原因终止。
    • 数据从一个热备份中恢复。
    • 在使用innodb_fast_shutdown = 2时,MySQL被关闭。
  • MySQL从一个InnoDB不存在或来自其他备份的备份被恢复。
  • MySQL从一个不一致的备份被恢复。
  • 在innodb_force_recovery = 6时,MySQL被启动。

 

原因

根本原因是InnoDB日志文件(重做日志)与数据文件不同步。

当InnoDB中发生更改,更改在被写入数据文件之前先被写入日志文件。在innodb_fast_shutdown = 0或1 (默认)的正常关闭期间,InnoDB会写一个比所有页中的位置更晚的检查点位置,因为它会将它们都刷掉,然后在服务器关闭之前写一个检查点。当innodb_fast_shutdown = 2时,它将它到达的位置写作检查点但不将任何页刷新到当前位置,所以会需要崩溃恢复。该崩溃恢复不会直接引起这个错误,但如果日志文件被删除就会引起,就像在崩溃后能删除它们一样。

 

当InnoDB从磁盘读取页并发出该错误信息时,它将页中位置与日志文件位置比较。

 

日志位置大于当前位置的页不会使用崩溃恢复,直到当前日志位置大于记录的位置。这表示崩溃恢复不能正确运行直到该问题被解决。

 

如果原因与以innodb_force_recovery = 6启动MySQL有关,这会清楚地显示在

MySQL错误日志中。更多信息参见Starting InnoDB on a Corrupted Database。

 

解决方案

选择以下方法之一:

  1. 如果你仍有它们,从备份或复制数据文件的位置恢复初始日志文件。在这个问题发生但没有任何更改的情况下,这会恢复所有数据。
  2. 使用mysqldump来备份数据,删除所有InnoDB表的ib_logfile*, ibdata*, *.ibd, 和*.frm;从转储中重建InnoDB表。一些数据丢失通常在这个情况下发生。
  3. 将所有InnoDB表转换为MyISAM,删除所有ib_logfile*, ibdata*,重启MySQL,并将表转回InnoDB。效果会与方案2相同,所以这个情况同样会发生数据丢失。
  4. 使用InnoDB存储引擎创建一个”work table” ,其大小与日志文件的总大小相同,将数据插入。然后删除这个工作表。通常在这个情况下会发生一些数据丢失。

 

方案2. 和 3. 可以合并。重要的是确保没有剩余的InnoDB表。合并方案2.和3.可以,例如使你将InnoDB表转换为MyISAM,其中没有具体InnoDB信息会丢失且使用mysqldump剩余的表能被备份并在重启后被恢复。

 

要在方案2., 3., 和4.中选择,你通常应该选择能在最短时间完成的方案,但是使用方案4会有较高的未检测问题的可能性,所以使用其他两个方案更好。

 

  1. 恢复初始日志文件

这也会从任何在使用的日志文件丢失的提交或未提交的事务中得到时间,所以这比之后会导致数据丢失的选项好多了。该选项应当被首先使用,除非错误发生后数据被更改了。那样的话,它对于接受旧工作的丢失并保留新工作造成的损失可能最低。

 

  1. 重载数据

在重新初始化所有InnoDB文件之后重载数据来确保一切回到一致状态。

 

步骤是:

  1. 转储所有InnoDB数据。你能用mysqldump 或 SELECT … INTO

OUTFILE完成。确保你也将schema信息如表定义和触发器包括在内。你不需要使用其他存储引擎来包括表。

  1. 停止MySQL。
  2. 删除所有InnoDB特定文件。文件的位置和名称将取决于你的MySQL 配置;默认是所有文件被datadir配置到MySQL数据目录下。你必须删除以下文件:
  • 共享的表空间文件(每个文件默认称为ibdata*)。
  • InnoDB日志文件(重做日志) ib_logfile*。
  • InnoDB表的表定义文件,*.frm。
  • InnoDB表的触发器文件 (*.TRN 和 *.TRG)。
  • InnoDB数据文件,*.ibd。
  1. 启动MySQL并等待InnoDB重新初始化。

当InnoDB完成创建新共享表空间和日志文件,你能在MySQL错误日志中看到。

  1. 重载InnoDB文件的数据和schema。

 

  1. 转换为MyISAM

将InnoDB表转换为MyISAM使你删除InnoDB共享表空间和日志文件,就像方案2,同时避免mysqldump 数据并恢复它。

特别是MySQL 5.1使用InnoDB 插件和MySQL 5.5及以上,你能利用快速索引创建来加速该过程。

 

步骤是:

  1. 对于每个InnoDB表,将表转换为MyISAM选项:

如果你在5.1或MySQL 5.5及以上使用InnoDB插件,还要从表中删除所有secondary索引。

 

删除secondary索引是性能优化,所以执行步骤的这个部分是可选的。对于很大的表,它们会获得最显著的性能提升。

 

例如:

 

ALTER TABLE t1
DROP INDEX va l1,
DROP INDEX val2,
ENGINE=MyISAM;

 

  1. 停止MySQL.
  2. 删除所有InnoDB特定文件。文件的位置和名称将取决于你的MySQL 配置;默认是所有文件被datadir配置到MySQL数据目录下。你必须删除以下文件:

 

共享的表空间文件(每个文件默认称为ibdata*).

 

InnoDB日志文件(重做日志) ib_logfile*.

 

  1. 启动MySQL并等待InnoDB重新初始化。

 

当InnoDB完成创建新共享表空间和日志文件,你能在MySQL错误日志中看到。

 

  1. 对于每个表:
  2. 将表重新转换为InnoDB并重新添加在步骤1被删除的唯一索引。你要在这个阶段操作,因为唯一索引不能利用快速索引创建,所以之后再添加它们会导致完整表的重建:

 

ALTER TABLE t1
ADD UNIQUE ( val1),
ENGINE=InnoDB;

 

  1. 将secondary索引重新添加到表中:

 

ALTER TABLE t1
ADD INDEX (v al2);

 

 

  1. 工作表

该方法会将当期日志序列带回未来报告的序列号之上。你能使用两个序列号之前的区别来估计你需要插入的数据量:

 

120207 13:30:29 InnoDB: Error: page 573441 log sequence number 22 697197707
InnoDB: is in the future! Current system log sequence number 5 2916730276.

 

号码与在SHOW ENGINE INNODB

STATUS (参见Document 1326051.1如何在计算中使用日志序列号的示例)中LOG部分的格式相同,所以两者之间的区别就是:

 

data required = ((22 ‐ 5) * 4 * 1024 * 1024 * 1024) + (697197707 ‐ 2916730276)

= 70794911463 bytes

 

即为66GB左右。

 

注:当使用带有InnoDB插件的MySQL 5.1或MySQL 5.5及以上,日志序列号会包括一个64-位号码而不是两个32-位号码。

 

要执行此方法,创建一个表并持续插入数据直到系统日志序列号赶上日志序列号。完成后,你能再次删除表。

 

这是linux的示例脚本,这会领先日志序列号大约20M:

 

#!/bin/sh
mysql ‐uroot test <<EOF
DROP TABLE IF EXISTS te mp_advance_lsn;
CREATE TABLE temp_advance_lsn (col1 TE XT) ENGINE=InnoDB;
INSERT INTO temp_advance_lsn VALUES (REPEAT('0123456789' , 1000));
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
DROP TABLE temp_advance_lsn;
EOF
mysql ‐uroot ‐e 'SHOW ENGINE INNODB STATUS;' \
| sed 's/\n/\n/g' \
| grep '^Log sequenc e'

 

 

 

如果你需要领先日志序列号(LSN)大于20M,那就不能删除表而是TRUNCATE它。然后重复添加数据和清空的进程知道你达到了LSN目标只。每次可以进行大于或小于20M,这只是如何通过较少的数据快速创建较大数据量的一个例子。创建然后破坏任何对你的情况最有效的数据块大小。

 

参考

https://dev.mysql.com/doc/refman/5.6/en/innodbparameters.

html#sysvar_innodb_fast_shutdown

https://dev.mysql.com/doc/refman/5.6/en/innodbmonitors.html

https://dev.mysql.com/doc/refman/5.6/en/selectinto.html

https://dev.mysql.com/doc/refman/5.6/en/innodbbackup.html

https://dev.mysql.com/doc/refman/5.6/en/forcinginnodbrecovery.html

https://dev.mysql.com/doc/refman/5.6/en/innodbparameters.html#sysvar_innodb_force_recovery

NOTE:1380282.1 MySQL Enterprise Backup (MEB) Fails; ERROR: Log scan was only able to reach to; InnoDB: log sequence number is in the future

NOTE:1326051.1 How to Check Whether innodb_log_file_size is Big Enough, InnoDB Redo Log

https://dev.mysql.com/doc/refman/5.6/en/innodbdatalogreconfiguration.html

Comment

*

沪ICP备14014813号

沪公网安备 31010802001379号