乐趣区

关于mysql:故障解析-生产环境遇到MySQL数据页损坏问题如何解决

当数据页毁坏,如何依据实例的健康状况抉择不同的策略定位损坏文件并复原。

作者:徐文梁

爱可生 DBA 成员,一个执着于技术的数据库工程师,次要负责数据库日常运维工作。善于 MySQL,Redis 及其他常见数据库也有涉猎;喜爱垂钓,看书,看风光,结交新敌人。

本文起源:原创投稿

  • 爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。

问题背景

四月份的时候,遇到一次实例异样 crash 的问题。过后数据库主动重启,未对生产造成影响,未做解决,然而还是记录了下错误信息,谬误日志中有如下信息:

InnoDB: End of page dump
InnoDB: Page may be an index page where index id is 8196
2023-04-11T07:57:42.508371+08:00 0 [ERROR] [FATAL] InnoDB: Apparent corruption of an index page [page id: space=3859, page number=842530] to be written to data file. We intentionally crash the server to prevent corrupt data from ending up in data files.
2023-04-11 07:57:42 0x7fe4d42cf080 InnoDB: Assertion failure in thread 140620788985984 in file ut0ut.cc line 921
InnoDB: We intentionally generate a memory trap.

因为过后主动复原了,并未器重这个问题,而后六月份的时候实例又 crash 了。查看报错信息,报错信息如下:

2023-06-23T04:32:36.538380+08:00 0 [ERROR] InnoDB: Probable data corruption on page 673268. Original record on that page;
(compact record)2023-06-23T04:32:36.538426+08:00 0 [ERROR] InnoDB: Cannot find the dir slot for this record on that page;
(compact record)2023-06-23 04:32:36 0x7fe2bf68f080 InnoDB: Assertion failure in thread 140611850662016 in file page0page.cc line 153
InnoDB: We intentionally generate a memory trap.

两次的报错信息很类似,呈现一次是偶尔,两次就值得器重了。尽管之前很侥幸未对生产造成影响,然而如果前面哪一天异样了导致实例无奈启动,那不就是妥妥的一个生产故障嘛,作为 DBA 要有忧患意思,必须要提前准备好应答之策,针对此类问题,该如何排查以及解决?通过查阅材料和向前辈求教,也算有所播种,想着如果有其他同学遇到相似问题也可作为参考,于是有了此文。

问题剖析

一般来说,数据页损坏,谬误日志中都会显示具体的 page number,其余状况暂不思考。在此前提下,依据实例状态能够将数据页损坏分为以下两种场景:

  1. 实例能失常启动
  2. 实例无奈失常启动

场景不同,解决办法也略有不同,上面别离开展详细分析:

场景一:实例能失常启动

此时借助通过谬误日志中的信息,能够通过查问元数据表获取数据页所属信息。思考生产环境信息安全,在测试环境建设测试表进行展现。

测试环境表构造如下:

mysql> use test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
 
Database changed
mysql> show create table t_user\G;
*************************** 1. row ***************************
       Table: t_user
Create Table: CREATE TABLE `t_user` (`id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` tinyint(4) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=178120 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
 
ERROR:
No query specified

依据错误信息中提醒的 page number 信息来查看数据页信息,查问形式如下:

mysql> use information_schema;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
 
Database changed
mysql> select * from  INNODB_BUFFER_PAGE where PAGE_NUMBER=1156 limit 10;
+---------+----------+-------+-------------+-----------+------------+-----------+-----------+---------------------+---------------------+-------------+-----------------+------------+----------------+-----------+-----------------+------------+---------+--------+-----------------+
| POOL_ID | BLOCK_ID | SPACE | PAGE_NUMBER | PAGE_TYPE | FLUSH_TYPE | FIX_COUNT | IS_HASHED | NEWEST_MODIFICATION | OLDEST_MODIFICATION | ACCESS_TIME | TABLE_NAME      | INDEX_NAME | NUMBER_RECORDS | DATA_SIZE | COMPRESSED_SIZE | PAGE_STATE | IO_FIX  | IS_OLD | FREE_PAGE_CLOCK |
+---------+----------+-------+-------------+-----------+------------+-----------+-----------+---------------------+---------------------+-------------+-----------------+------------+----------------+-----------+-----------------+------------+---------+--------+-----------------+
|       0 |       64 |   126 |        1156 | INDEX     |          0 |         0 | NO        |                   0 |                   0 |           0 | `test`.`t_user` | idx_name   |            515 |     15965 |               0 | FILE_PAGE  | IO_NONE | NO     |               0 |
+---------+----------+-------+-------------+-----------+------------+-----------+-----------+---------------------+---------------------+-------------+-----------------+------------+----------------+-----------+-----------------+------------+---------+--------+-----------------+
1 row in set (0.18 sec)

留神:查问 INNODB_BUFFER_PAGE 零碎表 会对性能有影响,因而不倡议随便在生产环境执行。

另外,如果谬误日志中有提醒 space idindex id 相干信息,则也能够通过如下形式(波及 INNODB_SYS_INDEXES 和 INNODB_SYS_TABLES 零碎表)进行查问:

mysql> select b.INDEX_ID, a.NAME as TableName, a.SPACE as Space,b.NAME as IndexName from INNODB_SYS_TABLES a,INNODB_SYS_INDEXES b where a.SPACE =b.SPACE and a.SPACE=126 and b.INDEX_ID=225;
+----------+-------------+-------+-----------+
| INDEX_ID | TableName   | Space | IndexName |
+----------+-------------+-------+-----------+
|      225 | test/t_user |   126 | idx_name  |
+----------+-------------+-------+-----------+
1 row in set (0.01 sec)

依据下面的查问后果,确定损坏的页是属于主键还是辅助索引,如果属于主键索引,因为在 MySQL 中索引即数据,则可能会导致数据失落,如果是辅助索引,删除索引重建即可。

场景二:实例无奈失常启动

此时能够通过两种形式尝试拉起实例。

办法一

应用 innodb_force_recovery 参数进行强制拉起 MySQL 实例。

失常状况下能够 innodb_force_force_recovery 值应该设置为 0。当紧急情况下实例无奈失常启动时能够尝试将其设置为 >0 的值,强制拉起实例而后将数据逻辑备份导出进行复原。innodb_force_recovery 值最高反对设置到 6,然而值为 4 或更大可能会永恒损坏数据文件。因而当强制 InnoDB 复原时,应始终以 innodb_force_recovery=1 结尾,并仅在必要时递增该值。

办法二

应用 inno_space 工具进行数据文件进行修复。

inno_space 是一个能够间接拜访 InnoDB 外部文件的命令行工具,能够通过该工具查看 MySQL 数据文件的具体构造,修复 corrupt page。更多参考

如果 InnoDB 表文件中的 page 损坏,导致实例无奈启动,能够尝试通过该工具进行修复,如果损坏的只是 leaf page,inno_space 能够将 corrupt page 跳过,从而保障实例可能启动,并且将绝大部分的数据找回。示例:

# 假如 MySQL 谬误日志中有相似报错如下:[ERROR] [MY-030043] [InnoDB] InnoDB: Corrupt page resides in file: .test/t_user.ibd, offset: 163840, len: 16384
[ERROR] [MY-011906] [InnoDB] Database page corruption on disk or a failed file read of page [page id: space=126, page number=1158]. You may have to recover from a backup.
 
# 通过如下形式进行修复:# 删除损坏的数据页中损坏局部。./inno -f /opt/mysql/data/3307/test/t_user.ibd   -d 10
 
# 更新损坏的数据页中 checksum 值。./inno -f /opt/mysql/data/3307/test/t_user.ibd   -u 10
  
启动 MySQL 服务。

问题总结

通过后面剖析,理解数据页损坏场景的解决形式。哪怕极其场景下,也能够做到从容不慌,尽可能少丢数据甚至可能不丢数据。然而如果是生产环境,尤其是金融行业,是无奈容忍失落一条数据的,比拟有可能这一条数据就波及几个小指标呢,因而,重要的事件说三遍, 肯定要备份!肯定要备份!肯定要备份!

更多技术文章,请拜访:https://opensource.actionsky.com/

对于 SQLE

爱可生开源社区的 SQLE 是一款面向数据库使用者和管理者,反对多场景审核,反对标准化上线流程,原生反对 MySQL 审核且数据库类型可扩大的 SQL 审核工具。

SQLE 获取

类型 地址
版本库 https://github.com/actiontech/sqle
文档 https://actiontech.github.io/sqle-docs/
公布信息 https://github.com/actiontech/sqle/releases
数据审核插件开发文档 https://actiontech.github.io/sqle-docs/docs/dev-manual/plugin…
退出移动版