技术分享 | MySQL 删库不跑路(建议收藏)
小伙伴们对数据库编程感兴趣吗?是否正在学习相关知识点?如果是,那么本文《技术分享 | MySQL 删库不跑路(建议收藏)》,就很适合你,本篇文章讲解的知识点主要包括MySQL、数据库。在之后的文章中也会多多分享相关知识点,希望对大家的知识积累有所帮助!
作者:洪斌
每个 DBA 是不是都有过删库的经历?删库了没有备份怎么办?备份恢复后无法启动服务什么情况?表定义损坏数据无法读取怎么办?
我曾遇到某初创互联网企业,因维护人员不规范的备份恢复操作,导致系统表空间文件被初始化,上万张表无法读取,花了数小时才抢救回来。
当你发现数据无法读取时,也许并非数据丢失了,可能是 DBMS 找不到描述数据的信息。
背景
先来了解下几张关键的 InnoDB 数据字典表,它们保存了部分表定义信息,在我们恢复表结构时需要用到。
SYS_TABLES 描述InnoDB表信息
CREATE TABLE `SYS_TABLES` ( `NAME` varchar(255) NOT NULL DEFAULT '', 表名 `ID` bigint(20) unsigned NOT NULL DEFAULT '0', 表id `N_COLS` int(10) DEFAULT NULL, `TYPE` int(10) unsigned DEFAULT NULL, `MIX_ID` bigint(20) unsigned DEFAULT NULL, `MIX_LEN` int(10) unsigned DEFAULT NULL, `CLUSTER_NAME` varchar(255) DEFAULT NULL, `SPACE` int(10) unsigned DEFAULT NULL, 表空间id PRIMARY KEY (`NAME`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
SYS_INDEXES 描述InnoDB索引信息
CREATE TABLE `SYS_INDEXES` ( `TABLE_ID` bigint(20) unsigned NOT NULL DEFAULT '0', 与sys_tables的id对应 `ID` bigint(20) unsigned NOT NULL DEFAULT '0', 索引id `NAME` varchar(120) DEFAULT NULL, 索引名称 `N_FIELDS` int(10) unsigned DEFAULT NULL, 索引包含字段的个数 `TYPE` int(10) unsigned DEFAULT NULL, `SPACE` int(10) unsigned DEFAULT NULL, 存储索引的表空间id `PAGE_NO` int(10) unsigned DEFAULT NULL, 索引的root page id PRIMARY KEY (`TABLE_ID`,`ID`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
SYS_COLUMNS 描述InnoDB表的字段信息
CREATE TABLE `SYS_COLUMNS` ( `TABLE_ID` bigint(20) unsigned NOT NULL, 与sys_tables的id对应 `POS` int(10) unsigned NOT NULL, 字段相对位置 `NAME` varchar(255) DEFAULT NULL, 字段名称 `MTYPE` int(10) unsigned DEFAULT NULL, 字段编码 `PRTYPE` int(10) unsigned DEFAULT NULL, 字段校验类型 `LEN` int(10) unsigned DEFAULT NULL, 字段字节长度 `PREC` int(10) unsigned DEFAULT NULL, 字段精度 PRIMARY KEY (`TABLE_ID`,`POS`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
SYS_FIELDS 描述全部索引的字段列
CREATE TABLE `SYS_FIELDS` ( `INDEX_ID` bigint(20) unsigned NOT NULL, `POS` int(10) unsigned NOT NULL, `COL_NAME` varchar(255) DEFAULT NULL, PRIMARY KEY (`INDEX_ID`,`POS`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
./storage/innobase/include/dict0boot.h 文件定义了每个字典表的index id,对应id的page中存储着字典表的数据。

这里我们需要借助undrop-for-innodb工具恢复数据,它能读取表空间信息得到page,将数据从page中提取出来。
# wget https://github.com/chhabhaiya/undrop-for-innodb/archive/master.zip # yum install -y gcc flex bison # make # make sys_parser
./sys_parser 读取表结构信息
sys_parser [-h
stream_parser 读取InnoDB page 从ibdata1或ibd 或分区表
# ./stream_parser You must specify file with -f option Usage: ./stream_parser -f <innodb_datafile> [-T N:M] [-s size] [-t size] [-V|-g] Where: -h - Print this help -V or -g - Print debug information -s size - Amount of memory used for disk cache (allowed examples 1G 10M). Default 100M -T - retrieves only pages with index id = NM (N - high word, M - low word of id) -t size - Size of InnoDB tablespace to scan. Use it only if the parser can't determine it by himself.</innodb_datafile>
c_parser 从innodb page中读取记录保存到文件
# ./c_parser Error: Usage: ./c_parser -4|-5|-6 [-dDV] -f <innodb page or dir> -t table.sql [-T N:M] [-b <external pages directory>] Where -f <innodb page> -- InnoDB page or directory with pages(all pages should have same index_id) -t <table.sql> -- CREATE statement of a table -o <file> -- Save dump in this file. Otherwise print to stdout -l <file> -- Save SQL statements in this file. Otherwise print to stderr -h -- Print this help -d -- Process only those pages which potentially could have deleted records (default = NO) -D -- Recover deleted rows only (default = NO) -U -- Recover UNdeleted rows only (default = YES) -V -- Verbose mode (lots of debug information) -4 -- innodb_datafile is in REDUNDANT format -5 -- innodb_datafile is in COMPACT format -6 -- innodb_datafile is in MySQL 5.6 format -T -- retrieves only pages with index id = NM (N - high word, M - low word of id) -b <dir> -- Directory where external pages can be found. Usually it is pages-XXX/FIL_PAGE_TYPE_BLOB/ -i <file> -- Read external pages at their offsets from <file>. -p prefix -- Use prefix for a directory name in LOAD DATA INFILE command</file></file></dir></file></file></table.sql></innodb></external></innodb>
接下来,我们演示场景的几种数据恢复场景。
场景1:drop table
是否启用了innodb_file_per_table其恢复方法有所差异,当发生误删表时,应尽快停止MySQL服务,不要启动。若innodb_file_per_table=ON,最好只读方式重新挂载文件系统,防止其他进程写入数据覆盖之前块设备的数据。
如果评估记录是否被覆盖,可以表中某些记录的作为关键字看是否能从ibdata1中筛选出。
grep WOODYHOFFMAN ibdata1
Binary file ibdata1 matches
也可以使用bvi(适用于较小文件)或hexdump -C(适用于较大文件)工具
以表sakila.actor为例
CREATE TABLE `actor` ( `actor_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, `first_name` varchar(45) NOT NULL, `last_name` varchar(45) NOT NULL, `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`actor_id`), KEY `idx_actor_last_name` (`last_name`) ) ENGINE=InnoDB AUTO_INCREMENT=201 DEFAULT CHARSET=utf8
首先恢复表结构信息
1.解析系统表空间获取page信息
./stream_parser -f /var/lib/mysql/ibdata1
2.新建一个schema,把系统字典表的DDL导入
cat dictionary/SYS_* | mysql recovered
3.创建恢复目录
mkdir -p dumps/default
4.解析系统表空间包含的字典表信息,
./c_parser -4f pages-ibdata1/FIL_PAGE_INDEX/0000000000000001.page -t dictionary/SYS_TABLES.sql > dumps/default/SYS_TABLES 2> dumps/default/SYS_TABLES.sql ./c_parser -4f pages-ibdata1/FIL_PAGE_INDEX/0000000000000002.page -t dictionary/SYS_COLUMNS.sql > dumps/default/SYS_COLUMNS 2> dumps/default/SYS_COLUMNS.sql ./c_parser -4f pages-ibdata1/FIL_PAGE_INDEX/0000000000000003.page -t dictionary/SYS_INDEXES.sql > dumps/default/SYS_INDEXES 2> dumps/default/SYS_INDEXES.sql ./c_parser -4f pages-ibdata1/FIL_PAGE_INDEX/0000000000000004.page -t dictionary/SYS_FIELDS.sql > dumps/default/SYS_FIELDS 2> dumps/default/SYS_FIELDS.sql
5.导入恢复的数据字典
cat dumps/default/*.sql | mysql recovered
6.读取恢复后的表结构信息
./sys_parser -pmsandbox -d recovered sakila/actor
由于5.x 版本 innodb引擎并非完整记录表结构信息,会丢失AUTO_INCREMENT属性、二级索引和外键约束,DECIMAL精度等信息。
若是mysql 5.5版本 frm文件被从系统删除,在原目录下touch与原表名相同的frm文件,还能读取表结构信息和数据。若只有frm文件,想要获得表结构信息,可使用mysqlfrm --diagnostic /path/to/xxx.frm,连接mysql会显示字符集信息。
- innodb_file_per_table=OFF
因为是共享表空间模式,数据页都存储在ibdata1,可以从ibdata1文件中提取数据。
1.获取表的table id,sys_table存有表的table id,sys_table表index id是1,所以从0000000000000001.page获取表id
./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000001.page -t dictionary/SYS_TABLES.sql | grep sakila/actor 000000000B28 2A000001430D4D SYS_TABLES "sakila/actor" 158 4 1 0 0 "" 0 000000000B28 2A000001430D4D SYS_TABLES "sakila/actor" 158 4 1 0 0 "" 0
2.利用table id获取表的主键id,sys_indexes存有表索引信息,innodb索引组织表,找到主键id即找到数据,sys_indexes的index id是3,所以从0000000000000003.page获取主键 id
./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000003.page -t dictionary/SYS_INDEXES.sql | grep 158 000000000B28 2A000001430BCA SYS_INDEXES 158 376 "PRIMARY" 1 3 0 4294967295 000000000B28 2A000001430C3C SYS_INDEXES 158 377 "idx\_actor\_last\_name" 1 0 0 4294967295 000000000B28 2A000001430BCA SYS_INDEXES 158 376 "PRIMARY" 1 3 0 4294967295 000000000B28 2A000001430C3C SYS_INDEXES 158 377 "idx\_actor\_last\_name" 1 0 0 4294967295
3.知道了主键id,就可以从对应page中提取表数据,并生成sql文件。
./c_parser -4f pages-ibdata1/FIL_PAGE_INDEX/0000000000000376.page -t sakila/actor.sql > dumps/default/actor 2> dumps/default/actor_load.sql
4.最后导入恢复的数据
cat dumps/default/*.sql | mysql sakila
- innodb_file_per_table=ON
这种情况恢复步骤与上述基本一致,但由于是独立表空间模式,数据页存储在各自的ibd文件,ibd文件删除了,无法通过ibdata1提取数据页,所以pages-ibdata1目录找不到数据页,stream_parser要从块设备中读取数据页信息。扫描完成后,在pages-sda1目录下提取数据。
./stream_parser -f /dev/sda1 -t 1000000k
场景2:Corrupted InnoDB table
在InnoDB表发生损坏,即使innodb_force_recovery=6也无法启动MySQL
日志中可能会出现类似报错
InnoDB: Database page corruption on disk or a failed InnoDB: file read of page 4.
此时的恢复策略需要将数据页从独立表空间中提取出,再删除表空间,重新创建表导入数据。
1.先获得故障表的主键index id
2.通过index id page获取到数据记录
select t.name, t.table_id, i.index_id, i.page_no from INNODB_SYS_TABLES t join INNODB_SYS_INDEXES i on t.table_id=i.table_id and t.name='test/sbtest1';
3.由于数据页可能有部分记录损坏,需要过滤掉“坏”的数据,保留好的数据
例如:前两行记录实际是“坏”数据,需要过滤掉。
root@test:~/recovery/undrop-for-innodb# ./c_parser -6f pages-actor.ibd/FIL_PAGE_INDEX/0000000000000015.page -t sakila/actor.sql > dumps/default/actor 2> dumps/default/actor_load.sql root@test:~/recovery/undrop-for-innodb# cat dumps/default/actor -- Page id: 3, Format: COMPACT, Records list: Invalid, Expected records: (0 200) 72656D756D07 08000010002900 actor 30064 "\0\0\0\0" "" "1972-09-20 23:07:44" 1050454E454C 4F50454755494E actor 19713 "ESSC▒" "" "2100-08-09 07:52:36" 00000000051E 9F0000014D011A actor 2 "NICK" "WAHLBERG" "2006-02-15 04:34:33" 00000000051E 9F0000014D0124 actor 3 "ED" "CHASE" "2006-02-15 04:34:33" 00000000051E 9F0000014D012E actor 4 "JENNIFER" "DAVIS" "2006-02-15 04:34:33" 00000000051E 9F0000014D0138 actor 5 "JOHNNY" "LOLLOBRIGIDA" "2006-02-15 04:34:33" 00000000051E 9F000001414141 actor 6 "AAAAA" "AAAAAAAAA" "2004-09-10 01:53:05" 00000000051E 9F0000014D016A actor 10 "CHRISTIAN" "GABLE" "2006-02-15 04:34:33" ...
可以在sql文件中加上筛选条件,比如:通过actor_id做范围筛选,再用新的sql文件读数据页。
CREATE TABLE `actor` ( `actor_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT /*!FILTER int_min_val: 1 int_max_val: 300 */, `first_name` varchar(45) NOT NULL, `last_name` varchar(45) NOT NULL, `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`actor_id`), KEY `idx_actor_last_name` (`last_name`) ) ENGINE=InnoDB AUTO_INCREMENT=201 DEFAULT CHARSET=utf8;
4.删除故障表文件,innodb_force_recovery=6启动MySQL,启动后删除元数据
5.创建新表导入恢复好的数据
疑问:如何知道丢失了多少记录?
读取数据页时开头会显示期望的记录数,最后会显示实际恢复的记录数,差值便是丢失记录数
-- Page id: 3, Format: COMPACT, Records list: Invalid, Expected records: (0 200) -- Page id: 3, Found records: 197, Lost records: YES, Leaf page: YES
场景3:磁盘或文件系统损坏如何恢复数据
这种情况下尽快保护损坏的块设备不要再写入,并用 dd 工具读取镜像数据用作恢复
本地方式
dd if=/dev/sdb of=/path/to/faulty_disk.img conv=noerror
远程方式
remote server> nc -l 1234 > faulty_disk.img local server> dd if=/dev/sdb of=/dev/stdout conv=noerror | nc a.b.c.d 1234
保存好磁盘镜像后,后续恢复操作参考场景2。
总结
1.千万不要在服务运行时把copy数据文件作为备份方式,看似备份了数据,但实际数据是不一致的。
2.正确的使用物理备份工具xtrabackup/meb或逻辑备份方式。
3.对备份数据要定期进行恢复验证测试。
希望你永远不会用到这些方法,做好备份,勤验证!
参考
https://twindb.com/how-to-rec...
https://twindb.com/recover-co...
https://twindb.com/take-image...
https://twindb.com/data-loss-...
https://twindb.com/repair-cor...
https://twindb.com/resolving-...
https://dev.mysql.com/doc/ref...
以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于数据库的相关知识,也可关注golang学习网公众号。

- 上一篇
- 《Tomcat和JVM的性能调优你真的学会了吗?》总结篇

- 下一篇
- 使用sql获取各数据库(MySQL、PostgreSQL、Oracle、MsSQL)的表结构
-
- 等待的毛衣
- 这篇文章真是及时雨啊,太细致了,写的不错,收藏了,关注大佬了!希望大佬能多写数据库相关的文章。
- 2023-02-15 21:23:15
-
- 苹果刺猬
- 受益颇多,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢老哥分享文章内容!
- 2023-02-06 09:22:33
-
- 凶狠的黄豆
- 太全面了,mark,感谢作者的这篇技术文章,我会继续支持!
- 2023-02-06 02:25:36
-
- 数据库 · MySQL | 2天前 |
- MySQL设置中文界面,超简单教程来了!
- 332浏览 收藏
-
- 数据库 · MySQL | 2天前 | mysql 索引提示
- MySQL进阶必看!FORCE/USE/IGNOREINDEX用法大揭秘
- 182浏览 收藏
-
- 数据库 · MySQL | 2天前 |
- 手把手教你写MySQL存储过程,小白也能轻松上手
- 163浏览 收藏
-
- 数据库 · MySQL | 2天前 | mysql group by
- MySQL分组查询优化:GROUPBY原理+索引优化超全解析
- 324浏览 收藏
-
- 数据库 · MySQL | 2天前 |
- MySQL设置中文语言,轻松拥有中文界面
- 211浏览 收藏
-
- 数据库 · MySQL | 2天前 |
- MySQL建库语句从入门到精通:创建数据库+设置字符集&排序规则(附实例)
- 176浏览 收藏
-
- 数据库 · MySQL | 2天前 |
- 从零开始学MySQL数据库操作,小白轻松变大神!
- 496浏览 收藏
-
- 数据库 · MySQL | 2天前 |
- MySQL插入日期到时间字段,轻松搞定日期格式
- 484浏览 收藏
-
- 数据库 · MySQL | 2天前 | mysql 数据压缩
- MySQL怎么实现高效压缩存储?表压缩+列式存储详细解读
- 272浏览 收藏
-
- 数据库 · MySQL | 2天前 | mysql JOIN优化
- MySQL优化JOIN操作:七大技巧教你提升关联查询速度
- 106浏览 收藏
-
- 数据库 · MySQL | 2天前 |
- MySQL出现中文乱码?超详细解决方案一次性搞定
- 211浏览 收藏
-
- 数据库 · MySQL | 2天前 |
- MySQL主从复制这样配!搞懂这些参数,replication稳了~
- 131浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- 茅茅虫AIGC检测
- 茅茅虫AIGC检测,湖南茅茅虫科技有限公司倾力打造,运用NLP技术精准识别AI生成文本,提供论文、专著等学术文本的AIGC检测服务。支持多种格式,生成可视化报告,保障您的学术诚信和内容质量。
- 25次使用
-
- 赛林匹克平台(Challympics)
- 探索赛林匹克平台Challympics,一个聚焦人工智能、算力算法、量子计算等前沿技术的赛事聚合平台。连接产学研用,助力科技创新与产业升级。
- 50次使用
-
- 笔格AIPPT
- SEO 笔格AIPPT是135编辑器推出的AI智能PPT制作平台,依托DeepSeek大模型,实现智能大纲生成、一键PPT生成、AI文字优化、图像生成等功能。免费试用,提升PPT制作效率,适用于商务演示、教育培训等多种场景。
- 58次使用
-
- 稿定PPT
- 告别PPT制作难题!稿定PPT提供海量模板、AI智能生成、在线协作,助您轻松制作专业演示文稿。职场办公、教育学习、企业服务全覆盖,降本增效,释放创意!
- 54次使用
-
- Suno苏诺中文版
- 探索Suno苏诺中文版,一款颠覆传统音乐创作的AI平台。无需专业技能,轻松创作个性化音乐。智能词曲生成、风格迁移、海量音效,释放您的音乐灵感!
- 60次使用
-
- golang MySQL实现对数据库表存储获取操作示例
- 2022-12-22 499浏览
-
- 搞一个自娱自乐的博客(二) 架构搭建
- 2023-02-16 244浏览
-
- B-Tree、B+Tree以及B-link Tree
- 2023-01-19 235浏览
-
- mysql面试题
- 2023-01-17 157浏览
-
- MySQL数据表简单查询
- 2023-01-10 101浏览