故障解析 | 生产环境遇到MySQL数据页损坏问题如何解决?

数字银月渡
• 阅读 707

当数据页破坏,如何根据实例的健康状况选择不同的策略定位损坏文件并恢复。

作者:徐文梁

爱可生 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_INDEXESINNODB_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...
点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
vsftpd虚拟用户配置
关于vsftpd与mysql结合的问题,参考了网上很多的方案,结果到最后,都是loginincorrect,很是纳闷(配置基本参考这个:http://www.howtoforge.com/vsftpd\_mysql\_debian\_etch\_p2,数据库创建参考别的,简单的那种)不小心发现每次数据库链接,mysql的id都会1,于是发现及时
Wesley13 Wesley13
3年前
MySQL日志体系详解
前言日志是MySQL数据库的重要组成部分。日志文件中记录着MySQL数据库运行期间发生的变化;也就是说用来记录MySQL数据库的客户端连接状况、SQL语句的执行情况和错误信息等。当数据库遭到意外的损坏时,可以通过日志查看文件出错的原因,并且可以通过日志文件进行数据恢复。MySQL的日志体系有如下几种分类:1.错误日志2.查询日志
Easter79 Easter79
3年前
TiDB 在 58 集团的应用与实践
作者介绍:刘春雷,58集团高级DBA,负责MySQL和TiDB的运维工作,TUGAmbassador。58集团业务种类繁多,目前包括的业务有58同城、赶集网、安居客、58金融公司、中华英才网、驾校一点通等,数据库种类包括MySQL、Redis、MongoDB、ES、TiDB。我们自己构建了“58云DB平台”,整合
Stella981 Stella981
3年前
DBeaver笔记
<divclass"markdown\_views"<p公司使用的是PostgreSQL数据库,可以使用pgAdmin或者DBeaver进行连接该数据库。个人更喜欢用DBeaver,因为其界面更加美观,操作也相对简单。对于习惯了eclipse的开发者来说,DBeaver绝对是个不错的选择。</p<blockquote<pDBeaver
Stella981 Stella981
3年前
Linux下利用Docker搭建MYSQL5.7
引言本文旨在介绍如何在Linux使用Docker快速搭建一个MYSQL环境,用于日常的开发调试,有需要的朋友可参考一下。配置MYSQL1)创建mysql配置目录创建配置目录mkdir p /etc/mysql/conf.d/创建数据和日志目录mkdir pv /opt/my
Stella981 Stella981
3年前
PostgreSQL物理坏块和文件损坏案例分享
作者简介王睿操,平安好医数据库架构岗,多年postgresql数据库运维开发工作。曾就职于中国民航信息,迪卡侬。对其他数据库产品也有一定的涉猎。背景笔者最近发现很多朋友经常遇到PostgreSQL坏块或者数据混乱的情况,网上中文资料比较少,于是整理了一下笔者遇到各种各样的报错以及解决方案案例一:物理坏块
Wesley13 Wesley13
3年前
InnoDB如何快速杀掉堵塞会话的思考
作者:八怪(高鹏)中亦科技数据库专家hu.com/p/d95bba14eddf如何快速找到并杀掉引起事务阻塞的session。 本文主要讲述MySQL5.7.29,也会加入和8.0的对比。一、问题起源我们在运维MySQL的过程中,肯定多多少少遇到过Innodbrowlock的问题,如果在线上遇到我们可能会看到一
Stella981 Stella981
3年前
MyRocks及其使用场景分析
出处:https://zhuanlan.zhihu.com/p/45652076作者:温正湖(任职于网易杭州研究院,从事数据库内核开发)MyRocks是一种经过空间和写性能优化的MySQL数据库,为您业务的数据库选型提供一种靠谱的选择。本文主要介绍什么是MyRocks,包括其功能特性,重点讲解MyRocks相比InnoDB的优势,详细
Wesley13 Wesley13
3年前
thinkphp 基本配置
12returnarray(34//定义数据库连接信息5'DB\_TYPE''mysql',//指定数据库是mysql67'DB\_HOST''localhost',89'DB\_NAME''uchome',//数据库名1011'DB\_USER''root
Wesley13 Wesley13
3年前
2014新炬实习岗位招聘
2014新炬实习岗位招聘Oracle工程师/初级DBA岗位职责:1、日常维护、故障处理,保障系统的健康稳定运行;2、快速响应用户需求,提供数据库技术支持;3、书写技术文档,总结工作经验和故障处理方案。岗位要求:1、本科或以上学历优秀应届生,计算机或相关专业,喜爱数据库技术者;2、熟系数据库体系结构;3、熟悉oracle的
3A网络 3A网络
2年前
工具 | 常用 MySQL 内核 Debug 技巧
工具|常用MySQL内核Debug技巧掌握MySQL内核源码的阅读和调试能力,不仅是数据库研发人员的日常,也是DBA进阶的必经之路。阅读本文你将了解:如何准备MySQL调试环境GDB调试入门及操作示例Trace文件调试及操作示例|一、准备Debug环境首先用源码编译安装一个用来调试的MySQL环境。开启DWI