背景
客户发消息说系统突然登录不上,验证码都出不来。当看到这个消息的时候觉得应该挺简单的,觉得大概率是应用程序挂了,决定临时使用重《重启》大法。登录应用服务进行查看发现服务在重启中,尝试重启也无效,就查看了相关日志发现是MySQL
服务连接不上了。接下来就只能看下MySQL 服务出现什么问题并想办法尝试解决。
问题
登录MySQL
服务器发现服务在启动中并且一直处于这个状态。停止服务MYSQL
服务并尝试重启,问题依然存在。MYSQL
的状态一直就是这个Server startup in progress
,如下所示:
mysqld.service – MySQL Server
Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; vendor preset: disabled)
Active: failed (Result: exit-code) since Wed 2024-05-29 13:57:58 CST; 2s ago
Docs: man:mysqld(8)
http://dev.mysql.com/doc/refman/en/using-systemd.html
Process: 1849957 ExecStartPre=/usr/bin/mysqld_pre_systemd (code=exited, status=0/SUCCESS)
Process: 1849986 ExecStart=/usr/sbin/mysqld $MYSQLD_OPTS (code=exited, status=2)
Main PID: 1849986 (code=exited, status=2)
Status: “Server startup in progress”
通过以上错误内容来看,疑似MySQL
配置参数出现问题,再通过了解得知客户并没有修改任何参数,这样由MySQL
配置导致的原因几乎不太可能。这样的话只能通过MySQL
的日志文件来看一看,能否发现有用的信息。找到MySQL
日志文件mysqld.log
,发现此文件非常大,57G
多。嗯!文件大也没有办法的,也得下载来看看。
日志下载完成后就发现了相关问题,MySQL
日志文件错误内容显示如下:
2024-05-29T13:24:15.276510Z 0 [ERROR] [MY-011971] [InnoDB] Tablespace ‘innodb_undo_002’ Page [page id: space=4294967278, page number=555] log sequence number 13653079935 is in the future! Current system log sequence number 13649029879.
2024-05-29T13:24:15.276563Z 0 [ERROR] [MY-011972] [InnoDB] Your database may be corrupt or you may have copied the InnoDB tablespace but not the InnoDB log files. Please refer to http://dev.mysql.com/doc/refman/8.0/en/forcing-innodb-recovery.html for information about forcing recovery.
2024-05-29T13:24:15.276576Z 0 [ERROR] [MY-011971] [InnoDB] Tablespace ‘innodb_undo_002’ Page [page id: space=4294967278, page number=524] log sequence number 13652559170 is in the future! Current system log sequence number 13649029879.
2024-05-29T13:24:15.276604Z 0 [ERROR] [MY-011971] [InnoDB] Tablespace ‘innodb_undo_002’ Page [page id: space=4294967278, page number=433] log sequence number 13654464601 is in the future! Current system log sequence number 13649029879.
引起以上问题的原因可能存在以下几种:
- 异常关机,例如:断电、强行关机
- MySQL服务被强行终止
无论是何种原因,问题最终还是要解决。
解决方案
根据上述错误内容以及互联网经验来看,需要恢复备份数据来解决。使用备份数据来恢复?使用时不可能使用的,不要问为什么不用备份数据,不是没有,是时间间隔比较长,之前的备份数据只能作为最后的备选方案。
在没有最近的备份数据的时候,需要将数据进行导出后再导入来解决,尝试导出数据有一个前提,那就是需要把MySQL服务先正常启动。根据提示在/etc/my.cnf
配置文件中[mysqld]
节点下新增innodb_force_recovery参数来尝试启动。
innodb_force_recovery
是 0
默认值(正常启动,不强制恢复)。允许的 innodb_force_recovery
的非零值为1
到 6
。 较大的值包括较小值的功能。例如,值3
包括值的所有功能1
和2
。
如果能够转储innodb_force_recovery
值为3
或者更少,那么你就相对安全了,只有一些数据 损坏的单个页面丢失。值为4
或更大为被认为是危险的,因为数据文件可能是永久性的 损坏。值6
被认为是巨大的,因为数据库页面处于过时状态,这反过来可能会引入对B-Tree
和其他数据库结构的更多损坏。
作为一项安全措施,当innodb_force_recovery
更大时,可防止 INSERT
、UPDATE
或 DELETE
操作。
- 1 (
SRV_FORCE_IGNORE_CORRUPT
)- 即使检测到损坏的页面,也允许服务器运行。试图跳过 损坏索引记录和页面,这有助于转储 表。
SELECT * FROM tbl_name
- 即使检测到损坏的页面,也允许服务器运行。试图跳过 损坏索引记录和页面,这有助于转储 表。
- 2 (
SRV_FORCE_NO_BACKGROUND
)- 阻止主控 线程和任何清除 线程从运行。如果发生意外退出 在吹扫操作期间, 此恢复值会阻止它。
- 3 (
SRV_FORCE_NO_TRX_UNDO
)- 崩溃恢复后不运行事务回滚。
- 4 (
SRV_FORCE_NO_IBUF_MERGE
)- 防止插入 缓冲区合并操作。如果它们会导致崩溃, 不做他们。不计算表统计信息。此值 可能会永久损坏数据文件。使用此值后,是 准备删除并重新创建所有二级索引。设置为只读。
- 5 (
SRV_FORCE_NO_UNDO_LOG_SCAN
)- 不看撤消 启动数据库时的日志:甚至处理未完成的事务 如承诺。此值可能会永久损坏数据文件。 设置为只读。
- 6 (
SRV_FORCE_NO_LOG_REDO
)- 不执行与恢复相关的重做日志前滚。此值可以 永久损坏的数据文件。将数据库页保留在 过时的状态,这反过来可能会带来更多的腐败 变成 B 树和其他数据库结构。设置为只读。
依次尝试使用1
、2
、3
、4
作为参数innodb_force_recovery
的值均启动失败,此时有一种非常不妙的感觉。终于在尝试5
的时候MySQL
成功启动,此时才有一种庆幸的感觉,终究还是给了个机会。参数innodb_force_recovery
配置示例如下:
[mysqld]
user=mysql
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
lower_case_table_names = 1 # 不区分大小写
sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO'
default-time_zone = '+8:00'
max_connections= 8000
max_user_connections= 10000
max_connect_errors= 4000
local_infile= ON
innodb_force_recovery=5
接下来就需要将数据导出,使用MySQL
自带的mysqldump导出数据库。
mysqldump -u账号 -p密码 -p3306 数据库名称> 保存文件名.sql
示例:
mysqldump -uroott -p123456 -p3306 mydata> mydata_bak.sql
接下来需要对MySQL数据库进行重新初始化,初始化需要进行如下步骤:
- 备份
mysql
数据目录(以防备用) - 彻底删除
mysql
数据目录 - 重新修改
/etc/my.cnf
配置文件,增加记录binlog
,去掉innodb_force_recovery
或置为0
- 执行命令重新初始化
mysql
MySQL初始化命令如下:
mysqld --initialize --user=mysql
mysql的数据目录权限需要是mysql:mysql
数据库初始化完毕以后,需要修改掉初始密码,再导入备份的数据内容。手动创建各个数据库,然后通过命令导入各数据库。
mysql -u用户名 -p密码 数据库名称 < 备份数据文件.sql
示例:
mysql -uroot -p123456 mydata < mydata_bak.sql
总结
生产环境最好要做定时备份,以防止意外情况发生。关机最好避免强制关机或者切断电源,虚拟机器避免强制挂起或者强制关机。这个客户没有对数据进行备份,负责实施的同事也没有设置定时备份,今后还是要避免这种请款的发生。
转载请注明:清风亦平凡 » MYSQL InnoDB引擎日志表损坏修复