点击上方"蓝字"

关注我们,享更多干货!

问题描述

生产环境MySQL数据库,架构是一主一从,可以双机切换,MySQL版本是5.7.24-enterprise-commercial-advanced-log。当时是存储MySQL的机房突然断电。主从机器重启后,MySQL都无法启动。两台机器都是同样错误,错误日志显示:

2020-10-27T00:58:41.032944Z 0 [Note] InnoDB: Log scan progressed past the checkpoint lsn 20171067203814
2020-10-27T00:58:41.136783Z 0 [Note] InnoDB: Doing recovery: scanned up to log sequence number 20171072446464
2020-10-27T00:58:41.222121Z 0 [Note] InnoDB: Doing recovery: scanned up to log sequence number 20171077689344
2020-10-27T00:58:41.312905Z 0 [Note] InnoDB: Doing recovery: scanned up to log sequence number 20171082932224
2020-10-27T00:58:41.401125Z 0 [Note] InnoDB: Doing recovery: scanned up to log sequence number 20171088175104
2020-10-27T00:58:41.493480Z 0 [Note] InnoDB: Doing recovery: scanned up to log sequence number 20171093417984
2020-10-27T00:58:41.579078Z 0 [Note] InnoDB: Doing recovery: scanned up to log sequence number 20171098660864
2020-10-27T00:58:41.608482Z 0 [Note] InnoDB: Doing recovery: scanned up to log sequence number 20171100172800
2020-10-27T00:58:41.608573Z 0 [ERROR] InnoDB: Ignoring the Redo log due to missing MLOG_CHECKPOINT between the checkpoint 20171067203814 and the end 20171100172800.
2020-10-27T00:58:41.608587Z 0 [ERROR] InnoDB: Plugin initialization aborted with error Generic error
2020-10-27T00:58:41.808775Z 0 [ERROR] Plugin ‘InnoDB’ init function returned error.
2020-10-27T00:58:41.808802Z 0 [ERROR] Plugin ‘InnoDB’ registration as a STORAGE ENGINE failed.
2020-10-27T00:58:41.808817Z 0 [ERROR] Failed to initialize builtin plugins.
2020-10-27T00:58:41.808823Z 0 [ERROR] Aborting

【问题分析】

MySQL源码:

/* Look for MLOG_CHECKPOINT. /
recv_group_scan_log_recs(group, &contiguous_lsn, false);
/ The first scan should not have stored or applied any records. */
ut_ad(recv_sys->n_addrs == 0);
ut_ad(!recv_sys->found_corrupt_fs);
if (recv_sys->found_corrupt_log && !srv_force_recovery) {
log_mutex_exit();
return(DB_ERROR);
}if (recv_sys->mlog_checkpoint_lsn == 0) {
if (!srv_read_only_mode&& group->scanned_lsn != checkpoint_lsn) {
ib::error() << "Ignoring the redo log due to missing"
" MLOG_CHECKPOINT between the checkpoint "
<< checkpoint_lsn << " and the end "
<< group->scanned_lsn << ".";
if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) {
log_mutex_exit();
return(DB_ERROR);
}
}

在MySQL 5.7版本以前,InnoDB恢复的时候需要依赖数据字典,因为InnoDB根本不知道某个具体的space对应的ibd文件是哪个,这些信息都是数据字典维护的。而且在恢复前,需要把所有的表空间全部打开,如果库中有数以万计的表,把所有表打开一遍,整个过程就会很慢。那么MySQL 5.7在这上面做了哪些改进呢?

其实很简单,针对上面的问题,InnoDB在Redo log中增加了两种Redo log的类型来解决。MLOG_FILE_NAME用于记录在checkpoint之后所有被修改过的信息(space, filepath);MLOG_CHECKPOINT用于标志MLOG_FILE_NAME的结束。

上面两种Redo log类型的添加,完美解决了前面遗留的问题,Redo log中保存了后续需要恢复的space和filepath对。因此在恢复的时候,只需要从checkpoint的位置往后扫描到MLOG_CHECKPOINT的位置,这样就能获取到需要恢复的space和filepath,在恢复过程中,只需要打开这些ibd文件即可,当然由于space和filepath的对应关系通过Redo存了下来,恢复的时候也不再依赖数据字典。

这里需要强调的一点就是MLOG_CHECKPOINT在每个checkpoint点中最多只存在一次,如果出现多次MLOG_CHECKPOINT类型的日志,则说明Redo已经损坏,InnoDB会报错。最多存在一次,那么会不会有不存在的情况呢?

答案是肯定的,在每次checkpoint过后,如果没有发生数据更新,那么MLOG_CHECKPOINT就不会被记录。所以只要简单查找下Redo log最新一个checkpoint后的MLOG_CHECKPOINT是否存在,就能判定上次MySQL是否正常关机。5.7版本的MySQL在InnoDB进行恢复的时候,也正是这样做的。MySQL 5.7在进行恢复的时候,一般情况下需要进行最多3次的Redo log扫描:

第一次Redo log的扫描,主要是查找MLOG_CHECKPOINT,不进行Redo log的解析,如果没有找到MLOG_CHECKPOINT,则说明InnoDB不需要进行recovery,后面的两次扫描可以省略,如果找到了MLOG_CHECKPOINT,则获取MLOG_FILE_NAME到指定列表,后续只需打开该链表中的表空间即可。

MySQL会在Redo log写一个一字节的最新的MLOG_CHECKPOINT 标记,用来标记在此之前的Redo都已checkpoint完成。因为异常断电,已找不到这个标记,整个Redo日志文件都会被忽略,innodb引擎启动失败。若是有备份的话,可以用备份恢复,再通过binlog追上最新数据。若是没有备份的话,通过救援模式启动数据库,导出数据,再导入恢复。

【解决过程】

1.查找备份文件

发现备份脚本在很久之前就没工作了,备份文件不是最新的。无法使用备份恢复。

2.删除Redo文件后启动

主库备份了Redo文件后,删除Redo文件,启动,失败。报错:

[ERROR] InnoDB: Page [page id: space=0, page number=7] log sequence number 20170595558339 is in the future! Current system log sequence number 10136858555422.
[ERROR] 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/5.7/en/forcing-innodb-recovery.html for information about forcing recovery.

LSN(log sequence number)日志序列号,占用8字节,LSN主要用于发生crash时对数据进行recovery,LSN是一个一直递增的整型数字,表示事务写入到日志的字节总量。

LSN不仅只存在于重做日志中,在每个数据页头部也会有对应的LSN号,该LSN记录当前页最后一次修改的LSN号,用于在recovery时对比重做日志LSN号决定是否对该页进行恢复数据。

上面的报错显示,数据在磁盘的page包含了较新的LSN,而此时系统system表空间头的LSN却是旧LSN。

3.设置innodb_force_recovery的参数值

先是设置innodb_force_recovery=3,启动失败,后设置为4,设置为5,设置为6,最终启动成功。

innodb_force_recovery的参数说明:

1(SRV_FORCE_IGNORE_CORRUPT):忽略检查到的corrupt页。
2(SRV_FORCE_NO_BACKGROUND):阻止主线程的运行,如主线程需要执行full purge操作,会导致crash。
3(SRV_FORCE_NO_TRX_UNDO):不执行事务回滚操作。
4(SRV_FORCE_NO_IBUF_MERGE):不执行插入缓冲的合并操作。
5(SRV_FORCE_NO_UNDO_LOG_SCAN):不查看重做日志,InnoDB存储引擎会将未提交的事务视为已提交。
6(SRV_FORCE_NO_LOG_REDO):不执行前滚的操作。

这时候,主库是只读状态,可以导出数据。

4.导出

假如数据量不大的情况下,可以直接全库导出。

因为当前数据目录大容量占有1.7T,恢复业务的紧迫性和磁盘空间的不足,跟研发业务部门沟通,打算恢复基本的数据,几个超百G的大表只恢复近期数据。

1)导出系统库MySQL库(里面含有原实例帐号数据)

mysqldump -uroot -p --databases mysql >mysql.sql

2)导出所有业务库表结构

mysql -uroot -p -e’show databases;’|grep -E -v “Database|mysql|sys|information_schema|performance_schema” | xargs mysqldump -uroot -p -d --triggers --routines --events --skip-lock-tables --set-gtid-purged=off --single-transaction --databases >alldb_desc.sql

3)导出所有业务库数据

忽略大表的数据没导出(根据实际情况,假如多个库的,分别导出,多个大表的,忽略多个),加快恢复速度。

mysqldump -uroot -p --set-gtid-purged=off -t 库名 --ignore-table=库名.忽略表名1 --ignore-table=库名.忽略表名2>库名.sql

4)导出大表部分数据

只保留近期数据(例如一个月的),过滤条件导出(根据实际情况,如果多个大表,多次导出)

mysqldump -uroot -proot --databases 库名 --tables 大表名 --where=‘ENTRY_DATE>=str_to_date(‘2020-10-01 00:00:00’,’%Y-%m-%d %H:%i:%s’)’ > 库名_大表名.sql

如果数据量大,导出时间长,可以考虑后台方式执行nohup 命令 &>>nohup.log

5.初始化实例

1)在从库机器上删除已存在数据和日志,初始化MySQL实例

mysqld --initialize --user=mysql

(备注:若是MySQL 5.6版本,初始化实例命令是mysql_install_db --user=mysql --defaults-file=/etc/my.cnf)

2)获取临时密码

cat mysqld.log|grep password
mysql -uroot -p临时密码

(备注:若是MySQL 5.6版本,初始化密码为空)

3)修改root密码

alter user ‘root’@‘localhost’ identified by ‘Root_123’;

6.导入

1)修改参数,加快导入

为了加快导入速度,建议关闭双1,甚至可以暂时关闭binlog。

修改前查参数值:

show GLOBAL variables like ‘foreign_key_checks’;
show GLOBAL variables like ‘unique_checks’;
show GLOBAL variables like ‘innodb_flush_log_at_trx_commit’;
show GLOBAL variables like ‘sync_binlog’;

执行如下加速操作:

SET GLOBAL foreign_key_checks=0;
SET GLOBAL unique_checks=0;
SET GLOBAL innodb_flush_log_at_trx_commit=0;
SET GLOBAL sync_binlog=0;

2)数据导入

在主库导出的备份文件,传输到从库机器上,依次导入:

mysql -uroot -p <mysql.sql

导入MySQL库后,记得执行flush privileges,重新加载权限表到内存:

mysql -uroot -p < alldb_desc.sql
mysql -uroot -p <库名.sql
mysql -uroot -p 库名 <库名_大表名.sql

如果数据量大,导入出时间长,可以考虑后台方式执行nohup 命令 &>>nohup.log。

数据恢复完成,指定这个实例为主库,恢复业务。后期重做从库,恢复数据同步复制。

MySQLdump一些常用参数说明

1、–all-databases , -A

含义:导出全部数据库。

示例:

mysqldump -uroot -p --all-databases

2、–add-drop-database

含义:每个数据库创建之前添加drop数据库语句。

示例:

mysqldump -uroot -p --all-databases --add-drop-database

3、–add-drop-table

含义:每个数据表创建之前添加drop数据表语句。(默认为打开状态,使用–skip-add-drop-table取消选项)

示例:

mysqldump -uroot -p --all-databases (默认添加drop语句)
mysqldump -uroot -p --all-databases –skip-add-drop-table (取消drop语句)

4、–databases, -B

含义:导出几个数据库。参数后面所有名字参量都被看作数据库名。

示例:

mysqldump -uroot -p --databases test mysql

5、–events, -E

含义:导出事件。

示例:

mysqldump -uroot -p --all-databases --events

6、–flush-logs

含义:开始导出之前刷新日志。

请注意:假如一次导出多个数据库(使用选项–databases或者–all-databases),将会逐个数据库刷新日志。除使用–lock-all-tables或者–master-data外。在这种情况下,日志将会被刷新一次,相应的所有表同时被锁定。因此,如果打算同时导出和刷新日志应该使用–lock-all-tables 或者–master-data 和–flush-logs。

示例:

mysqldump -uroot -p --all-databases --flush-logs

7、–flush-privileges

含义:在导出MySQL数据库之后,发出一条FLUSH PRIVILEGES 语句。为了正确恢复,该选项应该用于导出MySQL数据库和依赖MySQL数据库数据的任何时候。

示例:

mysqldump -uroot -p --all-databases --flush-privileges

8、–force

含义:在导出过程中忽略出现的SQL错误。

示例:

mysqldump -uroot -p --all-databases --force

9、–host, -h

含义:需要导出的主机信息

示例:

mysqldump -uroot -p --host=localhost --all-databases

10、–ignore-table

含义:不导出指定表。指定忽略多个表时,需要重复多次,每次一个表。每个表必须同时指定数据库和表名。

示例:

–ignore-table=database.table1 --ignore-table=database.table2 ……
mysqldump -uroot -p --host=localhost --all-databases --ignore-table=mysql.user

11、–master-data

含义:该选项将binlog的位置和文件名追加到输出文件中。如果为1,将会输出CHANGE MASTER 命令;如果为2,输出的CHANGE MASTER命令前添加注释信息。该选项将打开–lock-all-tables 选项,除非–single-transaction也被指定(在这种情况下,全局读锁在开始导出时获得很短的时间;其他内容参考下面的–single-transaction选项)。该选项自动关闭–lock-tables选项。

示例:

mysqldump -uroot -p --host=localhost --all-databases --master-data=1;
mysqldump -uroot -p --host=localhost --all-databases --master-data=2;

12、–no-create-db, -n

含义:只导出数据,而不添加CREATE DATABASE 语句。

示例:

mysqldump -uroot -p --host=localhost --all-databases --no-create-db

13、–no-create-info, -t

含义:只导出数据,而不添加CREATE TABLE 语句。

mysqldump -uroot -p --host=localhost --all-databases --no-create-info

14、–no-data, -d

含义:不导出任何数据,只导出数据库表结构。

示例:

mysqldump -uroot -p --host=localhost --all-databases --no-data

15、–password, -p

含义:连接数据库密码。

16、–port, -P

含义:连接数据库端口号。

17、–routines, -R

含义:导出存储过程以及自定义函数。

示例:

mysqldump -uroot -p --host=localhost --all-databases --routines

18、–single-transaction

含义:该选项在导出数据之前提交一个BEGIN SQL语句,BEGIN 不会阻塞任何应用程序且能保证导出时数据库的一致性状态。它只适用于多版本存储引擎,仅InnoDB。本选项和–lock-tables选项是互斥的,因为LOCK TABLES 会使任何挂起的事务隐含提交。要想导出大表的话,应结合使用–quick 选项。

示例:

mysqldump -uroot -p --host=localhost --all-databases --single-transaction

19、–tables

含义:覆盖–databases (-B)参数,指定需要导出的表名。

示例:

mysqldump -uroot -p --host=localhost --databases test --tables test
mysqldump -uroot -p --host=localhost --all-databases --triggers

20、–user, -u

含义:指定连接的用户名。

21、–where, -w

含义:只转储给定的WHERE条件选择的记录。请注意如果条件包含命令解释符专用空格或字符,一定要将条件引用起来。

示例:

mysqldump -uroot -p --host=localhost --all-databases --where=” user=’root’”

墨天轮原文链接:https://www.modb.pro/db/53688(复制链接至浏览器或点击文末阅读原文查看)

关于作者

黄超,云和恩墨MySQL技术顾问。985数据库管理专业科班出身,从事MySQL数据库技术服务10余年,持有MySQL认证、TiDB认证、GaussDB认证、PostgreSQL认证、openGauss认证等。现驻大厂负责MySQL数据库运维二线技术专家工作;热衷于运维故障处理、备份恢复、升级迁移、性能优化的学习与分享。

END

推荐阅读:267页!2020年度数据库技术年刊

推荐下载:2020数据技术嘉年华PPT下载

2020数据技术嘉年华近50个PPT下载、视频回放已上传墨天轮平台,可在“数据和云”公众号回复关键词“2020DTC”获得!

你知道吗?我们的视频号里已经发布了很多精彩的内容,快去看看吧!↓↓↓

点击下图查看更多 ↓

云和恩墨大讲堂 | 一个分享交流的地方

长按,识别二维码,加入万人交流社群

请备注:云和恩墨大讲堂

  点个“在看”

你的喜欢会被看到❤

MLOG_CHECKPOINT缺失下紧急数据恢复相关推荐

  1. 偷梁换柱 | 无备份情况下的数据恢复实践(二)

    黄玮(Fuyuncat) ♥       资深 Oracle DBA,致力于数据库底层技术的研究,其作品获得广大同行的高度评价. 个人网站 www.HelloDBA.com 在 数据恢复实践(一)中, ...

  2. 偷梁换柱 | 无备份情况下的数据恢复实践

    作者简介 黄玮(Fuyuncat) 资深 Oracle DBA,致力于数据库底层技术的研究,其作品获得广大同行的高度评价. 个人网站 www.HelloDBA.com 在实际环境中,许多数据库环境并没 ...

  3. SqlServer无备份下误删数据恢复

    系统已上线,给客户修改bug的时候,使用delete语句删表数据,没想到库没切换成测试库.误删了正式库的数据,而且一次备份都没有做过,玩大了 不扯了,进入主题 网上很多方法,都是针对至少有一次备份的情 ...

  4. linux分区表导出与恢复,Linux下硬盘数据恢复与分区表恢复

    硬盘分区表或数据丢失,是非常让人头痛的事.所幸的是,现在有相当多的工具可以对数据进行恢复. 体验一下testdisk,恢复一个硬盘的分区表,找回重要数据.而整个过程,只花了不到10分钟,且不需要进行系 ...

  5. extundelete安装_Linux下高效数据恢复软件extundelete应用实战

    作为一名运维人员,保证数据的安全是根本职责,所以在维护系统的时候,要慎之又慎,但是有时难免会出现数据被误删除的情况,在这个时候改如何快速.有效地恢复数据呢?本文我们就来介绍一下Linux系统下常用的几 ...

  6. 误删除分区下的数据恢复

    相信有不少的朋友都误删过分区,那么我们可以把误删掉的分区再找回来么,答案是可以的.这里我们需要用到数据恢复的知识.具体知识讲解参考岳老师的"解读分区表的秘密"一文,链接地址:htt ...

  7. 信仰缺失下的迷茫 (白岩松)

    一 这个社会的底线正不断地被突破,奶粉中可以有三聚氰胺:蔬菜中可以有伤人的农药:仅仅因为自己不舒服便可以夺走与自己无关人的性命:为了钱,可以随时欺骗,只要于己有利,别人,便只是一个可供踩踏的梯子.理想 ...

  8. Mysql数据库数据恢复,mysql灾后数据重建,linux环境下mysql数据恢复

    本次数据库灾难目前原因由于猜测是由于短时间内大量数据写入导致的数据库异常关闭,部分数据文件损坏,此恢复操作必须基于frm和idb文件没有受损的情况下尽可能的恢复数据,当然,mysql服务也必须能正常启 ...

  9. 【服务器数据恢复】reiserfs文件系统下RAID5数据恢复案例

    服务器数据恢复环境: 某品牌服务器由4块SAS硬盘组成RAID5,Linux操作系统: 存储分区结构:boot分区+LVM卷(划分了一个reiserfs文件系统,作为根分区)+swap分区. 服务器故 ...

最新文章

  1. 唏嘘!程序员,你的年底KPI完不成的原因找到了!
  2. vue更新数组和对象
  3. Java并发编程 Volatile关键字解析
  4. SAP 电商云 Spartacus UI B2B checkout 点击 Continue 不能跳转到下一页面
  5. 微信公众号之微信买单
  6. Web 前端框架分类解读
  7. java aspectj_Java:AspectJ的异常翻译
  8. Caffe框架-入门浅谈及碰到的坑
  9. caml library
  10. 可视化数据库监控警报工具 —— Sampler
  11. docker网段冲突导致主机网络异常处理
  12. sqlite数据库的版本更替
  13. 刚性PCB和柔性PCB的区别
  14. centos7常用快捷键
  15. 国外有哪些知名的游戏资讯网站或博客?
  16. 奇偶校验的快捷判断方法---按位异或
  17. 中国石油大学(北京)-《外国文学作品选读》第一阶段在线作业
  18. 写了Bug,误执行 rm -fr /*,我删删删删库了,要跑路吗?
  19. MySQL-数据表创建命令
  20. 语言表达逻辑训练,让你说话更条理清晰

热门文章

  1. PowerShell中的环境变量
  2. 未来集市广告_为什么广告的未来是开放的
  3. 您不会相信Buzzfeed如何处理变更管理
  4. facebook 开源_Facebook开源主管开放
  5. java/05/(Swing包)窗体,组件,布局管理器,面板,监听事件
  6. LeetCode 123. 买卖股票的最佳时机 III(Best Time to Buy and Sell Stock III)
  7. 文末送书 | WAF 那些事
  8. 设计模式 接口隔离原则
  9. node事件循环 EventEmitter 异步I/O Buffer缓冲区 模块
  10. 二.开发记录之派勤工控机远程使用和ubuntu和ROS环境配置