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丢表的问((bug62146, bug62100)上用过,能恢复。当然,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版本。
升级过程,一定要仔细,尽量避免不必要的麻烦。
文中的一句话“要知道,mysqladmin是不管mysqld有没有真正shutdown完成就返回。”,那怎么确定MySQL 已经真正地shutdown了呢?
有很多方法来确定mysqld是否被shutdown, ps进程、 看error.log、看mysql.pid等,都可以的。
mysqladmin shutdown 关闭数据库这个应该是MySQL中首选的关闭数据库方式