MySQL5.1,5.5で3~4年運用していたDatabaseをMySQL5.6にアップグレードして約3ヶ月が経過したが、その間InnoDBのDatabase pageが壊れる事象が発生している。
利用したのは次のバージョンのMySQL5.6系。
mysql-community-server.x86_64 5.6.17-4.el6
mysql-community-server.x86_64 5.6.19-2.el6

MySQLを利用しているLAMPアプリがエラーになり、妙なエラーが出ている。

PHP Warning:  PDOStatement::execute(): MySQL server has gone away in xxx.php on line 78

/var/log/mysql.logを見てみると、次のようなログ出力され、ログも肥大化している。

2回目に壊れた時。

InnoDB: Database page corruption on disk or a failed
InnoDB: file read of page 78481.
InnoDB: You may have to recover from a backup.
2014-05-27 04:19:31 7f25e7fff700 InnoDB: Page dump in ascii and hex (16384 bytes):

nnoDB: End of page dump
2014-05-27 04:19:31 7f25e7fff700 InnoDB: uncompressed page, stored checksum in field1 881243038, calculated checksums for field1: crc32 3412082521, innodb 47248
3626, none 3735928559, stored checksum in field2 910221241, calculated checksums for field2: crc32 3412082521, innodb 910221241, none 3735928559, page LSN 0 396
9463722, low 4 bytes of LSN at page end 3969463722, page number (if stored to page already) 78481, space id (if created with >= MySQL-4.1.1 and stored already)
405
InnoDB: Page may be an index page where index id is 904
InnoDB: (index "PRIMARY" of table "db01"."tbl01")
InnoDB: Database page corruption on disk or a failed
InnoDB: file read of page 78481.
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.
InnoDB: If the corrupt page is an index page
InnoDB: you can also try to fix the corruption
InnoDB: by dumping, dropping, and reimporting
InnoDB: the corrupt table. You can use CHECK
InnoDB: TABLE to scan your table for corruption.
InnoDB: See also http://dev.mysql.com/doc/refman/5.6/en/forcing-innodb-recovery.html
InnoDB: about forcing recovery.
InnoDB: Database page corruption on disk or a failed
InnoDB: file read of page 78481.
InnoDB: You may have to recover from a backup.

3回目に壊れた時。

InnoDB: End of page dump
2014-06-12 04:03:28 7f03d7e15700 InnoDB: uncompressed page, stored checksum in field1 1783178167, calculated checksums for field1: crc32 6456952, innodb 4192140
979, none 3735928559, stored checksum in field2 1335262152, calculated checksums for field2: crc32 6456952, innodb 1335262152, none 3735928559, page LSN 3 36580
04246, low 4 bytes of LSN at page end 3658004246, page number (if stored to page already) 64982, space id (if created with >= MySQL-4.1.1 and stored already) 10
56
InnoDB: Page may be an index page where index id is 1980
InnoDB: (index "url" of table "db01"."tbl01")
InnoDB: Database page corruption on disk or a failed
InnoDB: file read of page 64982.
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.
InnoDB: If the corrupt page is an index page
InnoDB: you can also try to fix the corruption
InnoDB: by dumping, dropping, and reimporting
InnoDB: the corrupt table. You can use CHECK
InnoDB: TABLE to scan your table for corruption.
InnoDB: See also http://dev.mysql.com/doc/refman/5.6/en/forcing-innodb-recovery.html
InnoDB: about forcing recovery.
InnoDB: Database page corruption on disk or a failed
InnoDB: file read of page 64982.
InnoDB: You may have to recover from a backup.
2014-06-12 04:03:28 7f03d7e15700 InnoDB: Page dump in ascii and hex (16384 bytes):
 len 16384; hex 6a4923b70000fdd60000fb450001007700000003da08b71645bf000000000000000000000420005e3f208177007e002c0000000201730174000000000009fa660000000000000000
07bc00000000000000000000000000000000000000000100020047696e66696d756d0005000b00007375707

どちらも、同じDB・テーブルのINDEXが格納されているPageのようだ。
check tableしてみると、doesn't existと、よくわからない結果が...

mysql> check table db01.tbl01;
+------------+-------+----------+----------------------------------+
| Table      | Op    | Msg_type | Msg_text                         |
+------------+-------+----------+----------------------------------+
| db01.tbl01 | check | Error    | Table 'db01.tbl01' doesn't exist |
| db01.tbl01 | check | status   | Operation failed                 |
+------------+-------+----------+----------------------------------+

このテーブルには次のようなカラムとINDEXがある。

DROP TABLE IF EXISTS `tbl01`;
CREATE TABLE `tbl01` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `url` text NOT NULL,
  `dt` datetime NOT NULL,
  `host` text NOT NULL,
  `path` text NOT NULL,
  `title` text NOT NULL,
  `description` text NOT NULL,
  `rem` text NOT NULL,
  PRIMARY KEY (`id`),
  KEY `url` (`url`(255)),
  KEY `host` (`host`(255))
) ENGINE=InnoDB AUTO_INCREMENT=5872167 DEFAULT CHARSET=utf8;

このINDEXがまずいのだろうか?
※2度目に壊れた時には KEY `host` (`host`(255))→ KEY `host` (`host`(100))にしたが、再度壊れた。

PRIMARY KEY (`id`),
  KEY `url` (`url`(255)),
  KEY `host` (`host`(100))

あるいは、DISKが壊れかけていて、格納したデータが本当に壊れているのかもしれない。
このDB・TABLEはこのサーバーの中でも最も大きいDBなので、そのDISK領域にぶつかりやすいのか?
しかし、必ずINDEXというのもヘンである。データ領域が壊れてもおかしくない。
あるいは、MySQLのConfigがよろしくないのかもしれない。

とりあえず復旧させて、様子を見ることにしました。
MySQLを復旧させた手順は次の通り。
1度目は全Databaseをダンプ、リストアしたが、今回は該当のTABLEのみで復旧できた。
参考


14.19.2 Starting InnoDB on a Corrupted Database

  1. MySQLのSlaveサーバーのレプリケーションを止める。
  2. /etc/my.cnfを書き換え、mysqlを再起動する。
    [mysqld]
    innodb_force_recovery = 1
    
    /etc/init.d/mysqld restart
    
  3. 壊れたTABLEをダンプする。
    mysqldump --user root -p --opt --single-transaction --default-character-set=utf8 --hex-blob db01 tbl01 > tbl01_20140612.sql
    Enter password:
    
  4. 何かテーブルが壊れるような要因がありそうなら、変える。今回は、urlのindexサイズを小さくしてみた。
    DROP TABLE IF EXISTS `tbl01`;
    CREATE TABLE `tbl01` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `url` text NOT NULL,
      `dt` datetime NOT NULL,
      `host` text NOT NULL,
      `path` text NOT NULL,
      `title` text NOT NULL,
      `description` text NOT NULL,
      `rem` text NOT NULL,
      PRIMARY KEY (`id`),
      KEY `url` (`url`(100)),
      KEY `host` (`host`(100))
    ) ENGINE=InnoDB AUTO_INCREMENT=5872167 DEFAULT CHARSET=utf8;
    
  5. /etc/my.cnfをinnodb_force_recoveryを外し、mysqlを再起動する。
    [mysqld]
    #log-bin=mysql-bin
    ※復旧時にバイナリログが不要ならlog-binをコメントにする。
    
    #innodb_force_recovery = 1
    
    /etc/init.d/mysqld restart
    

    ※これをやらないと、Tableをインポート時にエラーが発生した。

    ERROR 1881 (HY000) at line 46: Operation not allowed when innodb_forced_recovery > 0.
    
  6. Tableをインポートする。
    mysql --user root -p db01 < tbl01_20140612.sql
    Enter password:
    
  7. /etc/my.cnfをlog-binを戻し、mysqlを再起動する。
    [mysqld]
    log-bin=mysql-bin
    
    /etc/init.d/mysqld restart
    
  8. MySQLのSlaveサーバーのレプリケーションを再度開始。

原因がわからないので、また再発した場合の復旧メモとして記録しておこう。

追記)
その後、2回MySQLのDBが同様にクラッシュ。
結局、
・全DBをダンプ
・MySQL関連のモジュールを yumでアンインストール
・再度MySQLをインストール
・DBリストア
を行ったところ、クラッシュしなくなった。