MySQL升级导致page corruption而无法启动问题

昨天遇到一个问题, 同事反馈:MySQL同版本升级(升级到trunk中某一版本,非最新),重启后mysqld报错:

121029 16:53:06 InnoDB: The InnoDB memory heap is disabled
121029 16:53:06 InnoDB: Mutexes and rw_locks use GCC atomic builtins
121029 16:53:06 InnoDB: Compressed tables use zlib 1.2.3
121029 16:53:06 InnoDB: Initializing buffer pool, size = 17.0G
121029 16:53:07 InnoDB: Completed initialization of buffer pool
InnoDB: Database page corruption on disk or a failed
InnoDB: file read of page 5.
InnoDB: You may have to recover from a backup.
121029 16:53:08  InnoDB: Page dump in ascii and hex (16384 bytes):
len 16384; hex 0208a95200000 …
InnoDB: End of page dump
121029 16:53:08  InnoDB: Page checksum 2125935208 (32bit_calc: 34122066), prior-to-4.0.14-form checksum 2677217926
InnoDB: stored checksum 34122066, prior-to-4.0.14-form stored checksum 2677217926
InnoDB: Page lsn 941 1982560179, low 4 bytes of lsn at page end 1982560179
InnoDB: Page number (if stored to page already) 5,
InnoDB: space id (if created with >= MySQL-4.1.1 and stored already) 0
InnoDB: Page may be a transaction system page
InnoDB: Database page corruption on disk or a failed
InnoDB: file read of page 5.
InnoDB: You may have to recover from a backup.
InnoDB: It is also possible that your operating
InnoDB: system has corrupted its own file cache
InnoDB: and rebooting your computer removes the
InnoDB: error.

很明显,ibdata中的page5 checksum不一致,导致buf_page_io_complete中buf_page_is_corrupted触发断言失败:

121029 16:53:08  InnoDB: Assertion failure in thread 140162205325056 in file buf0buf.c line 4020

之前就是Percona-5.5,升级无非是打的一些补丁,出现这种问题真不让人理解。

首先要做的,就是赶紧恢复数据。尝试回退到原来的mysqld,启动还是失败,同样的错误,这说明与打补丁的mysqld没有关系。那么,我们尝试:

1. innodb_corrupt_table_action. 从assert改为warn.

2. innodb_force_recovery (1~6)

3. 拷贝data/<database>/<table>.ibd恢复

现实的情况是:

1)还是crash,换了个地方,说明损坏的还有点严重。

2)和3)并行地做:

方法2)因为bug923820而被阻塞启动不成功,升级到trunk中稍微新些的版本,这就不是个问题了。从恢复级别从1开始重试,直到6才能启动成功,每个过程时间超长,10多分钟吧。

3)是通过经典的方法来做,其中discard/import tablespace导致table_space_id报错的方法有两种,一种是创建表让table_space_id涨到当前表小1的值,另外一种方法是直接修改table_space_id。这两种方法在去年DDL丢表的问((bug62146bug62100)上用过,能恢复。当然,percona有工具是通过第一种方法来做的,偷懒的话可以直接percona的工具,原理大致相同。

对于3),当多个表有问题时,真是件痛苦的事,必须要从第一个创建的表为discard/import tablespace对象,这样才能从error.log中看到ibdata的词典中的table_space_id涨到什么值,需要改到什么值。批量的表修复,如果后面有个表的table_space_id比当前词典的小,那就完蛋了,必须重新开始。因为我们的DB的表是批量建的,让我有一丝希望,但后面实在是太痛苦了,方法2)先做完,解脱。要提一句,当用此种方法恢复时,恢复成功后要check下ibdata,怎么做,参考CREATE TABLE innodb_tablespace_monitor (a INT) ENGINE = InnoDB;

对于2),还是首选,恢复了就赶紧dump吧。2)恢复不了,没有办法才去做3)。

到此,mysqld启动问题算是解决。但为什么升级后不能启动,究其原因,才发现是操作过程中的一个小错误导致: mysqldadmin shutdown返回即开始移动数据,导致之前的文件被删除,用这部分不完整的数据启动mysqld导致问题发生。要知道,mysqladmin是不管mysqld有没有真正shutdown完成就返回。

另外,看了下jira上记录了去年也发生这么一起事故:

nnoDB: Database page corruption on disk or a failed
InnoDB: file read of page 7.
InnoDB: You may have to recover from a backup.
111201 10:22:14 InnoDB: Page dump in ascii and hex (16384 bytes):

但不同的是,导致的原因是备库是用物理备份恢复,从MySQL-5.0 版本升级到Percona-5.5版本。

升级过程,一定要仔细,尽量避免不必要的麻烦。

3 Comments

  1. 文中的一句话“要知道,mysqladmin是不管mysqld有没有真正shutdown完成就返回。”,那怎么确定MySQL 已经真正地shutdown了呢?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>