undrop-for-innodb
undrop是一款针对mysql innodb的数据恢复工具,通过扫描文件或磁盘设备,然后解析innodb数据页进而恢复丢失的数据,对于drop、truncate以及文件损坏都很有帮助。本文介绍drop操作后表结构的恢复过程。
部署
软件包下载
shell> git clone https://github.com/twindb/undrop-for-innodb.git
依赖包安装
#此处安装必须的编译环境
shell> yum install make gcc flex bison
编译
shell> make
shell> ls
check_data.c c_parser.o innochecksum.c Makefile recover_dictionary.sh sql_parser.o stream_parser.o test.sh
check_data.o dictionary innochecksum_changer print_data.c sakila sql_parser.y sys_parser.c vagrant
c_parser fetch_data.sh lex.yy.c print_data.o sql_parser.c stream_parser tables_dict.c
c_parser.c include LICENSE README.md sql_parser.l stream_parser.c tables_dict.o
到这里部署过程就结束了,这里介绍下几个重要的文件及目录:
dictionary目录。存放字典sql脚本,用于恢复表结构的几张核心字典表的DDL语句
sakila目录。测试schema
stream_parser。可执行文件,用于扫描文件或者磁盘设备,目的是找出符合innodb格式的数据页,按照index_id进行组织
c_parser。可执行文件,用于解析innodb数据页,获取行记录
sys_parser。可执行文件,通过字典表记录恢复目标表的表结构
环境准备
这款工具支持主流linux的4/5/6/7版本,mysql支持5.6/5.7。本文中测试的环境是centos7+MySQL5.7
[root@mydocker-test1 undrop-for-innodb]# cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)
mysql> select version();
+------------+
| version() |
+------------+
| 5.7.20-log |
+------------+
1 row in set (0.00 sec)
接下来准备测试表
mysql> show create table t1\G
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`id` int(11) NOT NULL,
`name` varchar(20) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
row in set (0.00 sec)
mysql> select * from t1;
+----+----------+------+
| id | name | age |
+----+----------+------+
| 1 | zhangsan | 18 |
| 2 | lisi | 21 |
| 3 | jack | 17 |
| 4 | alei | 25 |
+----+----------+------+
4 rows in set (0.00 sec)
然后进行删除操作
mysql> drop table t1;
Query OK, 0 rows affected (0.32 sec)
innodb数据字典介绍
在做恢复演示之前,首先插播一段innodb数据字典的介绍。
innodb数据字典存储在系统表空间,主要是用于记录innodb核心的对象信息,比如表、索引、字段等。字典的本质是REDUNDANT行格式的innodb表,并且对用户不可见。
为了便于用户查看,innodb提供了一系列的字典视图,视图提供的信息和字典表完全相同,这一部分内容我们可以在information_schema中找到。如下,
mysql> use information_schema -A;
Database changed
mysql> show tables where Tables_in_information_schema like '%innodb_sys%';
+------------------------------+
| Tables_in_information_schema |
+------------------------------+
| INNODB_SYS_DATAFILES |
| INNODB_SYS_VIRTUAL |
| INNODB_SYS_INDEXES |
| INNODB_SYS_TABLES |
| INNODB_SYS_FIELDS |
| INNODB_SYS_TABLESPACES |
| INNODB_SYS_FOREIGN_COLS |
| INNODB_SYS_COLUMNS |
| INNODB_SYS_FOREIGN |
| INNODB_SYS_TABLESTATS |
+------------------------------+
10 rows in set (0.00 sec)
接下来介绍几张对于数据恢复较为关键的几张字典表。
1、SYS_TABLES。对应视图INNODB_SYS_TABLES,提供innodb表的元数据信息,其中几个关键字段如下,
TABLE_ID。innodb表的标识id,这是组织字典信息的关键
NAME。表名
N_COLS。字段数量,包含innodb的三个隐藏字段,以及虚拟字段
FILE_FORMAT。文件格式
ROW_FORMAT。行格式
2、SYS_COLUMNS。对应视图INNODB_SYS_COLUMNS,提供innodb表中字段的元数据信息,其中关键字段如下,
TABLE_ID。表id,和sys_tables关联
NAME。字段名
POS。0开始的序号,表示字段在表中的位置
MTYPE/PRTYPE。字段类型,这是innodb的表示方法,前者是主体类型,后者是精确类型
LEN。字段长度
MTYPE
Stands for “main type”. A numeric identifier for the column type. 1 = VARCHAR, 2 = CHAR, 3 = FIXBINARY, 4 = BINARY, 5 = BLOB, 6 = INT, 7 = SYS_CHILD, 8 = SYS, 9 = FLOAT, 10 = DOUBLE, 11 = DECIMAL, 12 = VARMYSQL, 13 = MYSQL, 14 = GEOMETRY.
PRTYPE
The InnoDB “precise type”, a binary value with bits representing MySQL data type, character set code, and nullability.
3、SYS_INDEXES。对应视图INNODB_SYS_INDEXES,提供索引定义信息,关键字段如下,
INDEX_ID。索引id,这是组织数据的关键。innodb所有的数据都是基于B+tree进行组织,因此B+tree叶节点存储表数据以及二级索引数据,每个叶节点数据页存储index_id信息以标识所属的index。undrop-for-innodb工具扫描innodb数据页后通过index_id对数据页进行重组
NAME。索引名
TABLE_ID。表id,与sys_tables关联
PAGE_NO。B+tree索引根节点的页号
4、SYS_FIELDS。对应视图INNODB_SYS_FIELDS,提供索引定义中的字段信息。此信息可用于恢复二级索引
INDEX_ID。索引id,和SYS_INDEXES关联
NAME。字段名
POS。0开始的序号,表示字段在索引定义中的位置
有关字典表的描述,详细可参阅官方文档:
https://dev.mysql.com/doc/refman/5.7/en/innodb-information-schema-system-tables.html
表结构恢复
接下来进行误删除操作后的数据恢复过程,本文介绍第一步,表结构的恢复,主要理解几张字典表的逻辑关系。
恢复表结构的实质是恢复被删除的字典数据,因此是需要在ibdata文件中字典数据不被重写的前提下进行,咱们这里在发生删除操作后首先关闭数据库实例。
[root@mydocker-test1 undrop-for-innodb]#
[root@mydocker-test1 undrop-for-innodb]# mysqladmin -h127.0.0.1 -uroot -P3310 -p shutdown
Enter password:
然后解析ibdata文件,扫描出所有符合innodb格式的数据页,结果会按照index_id进行重新组织,
shell> ./stream_parser -f /home/my3310/data/ibdata1
此时会生成一个ibdata文件解析后的目录结构,接下来使用c_parser解析数据页,获取丢失的元数据行记录
#解析四张关键字典表的数据,获取已删表的数据结构。
#所有字典表的index_id是硬编码的,这里01是SYS_TABLES,02是SYS_COLUMNS,03是SYS_INDEXES,04是SYS_FIELDS。
shell> mkdir -p dumps/default
shell> ./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000001.page -t dictionary/SYS_TABLES.sql >./dumps/default/SYS_TABLES 2>./dumps/default/SYS_TABLES.sql
shell> ./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000003.page -t dictionary/SYS_INDEXES.sql >./dumps/default/SYS_INDEXES 2>./dumps/default/SYS_INDEXES.sql
shell> ./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000002.page -t dictionary/SYS_COLUMNS.sql >./dumps/default/SYS_COLUMNS 2>./dumps/default/SYS_COLUMNS.sql
shell> ./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000004.page -t dictionary/SYS_FIELDS.sql >./dumps/default/SYS_FIELDS 2>./dumps/default/SYS_FIELDS.sql
这里有几个选项:
-4Df 。最前面的数字4/5表示行格式REDUNDANT/COMPACT,D表示获取被删除的记录,f后面指定文件
-t 。指定建表语句,生成的结果会根据表结构进行组织
注意:
这里有个地方需要注意,就是这个输出的文件路径./dumps/default,这是工具里代码固定的,因此在此步骤之前我们必须要创建这个目录 $BASEDIR/dumps/default
接下来查看下解析结果,
shell> cd dumps/default/
shell> ls
SYS_COLUMNS SYS_COLUMNS.sql SYS_FIELDS SYS_FIELDS.sql SYS_INDEXES SYS_INDEXES.sql SYS_TABLES SYS_TABLES.sql
现在我们看到的就是恢复出来的元数据记录,每张字典表有两个文件,和表名同名的文件已文本形式保存行记录,另一个SQL文件是根据表结构生成的loda data语句。
接下来在本地库上将字典数据恢复出来,注意这里需要是另外一个mysql实例,因为为了避免数据文件被复写,我们此前已经将事故实例停止了服务。
#根据工具安装文件下的dictionary目录中的SQL脚本创建字典表
mysql> create database dictionary;
mysql> use dictionary;
mysql> source /root/undrop-for-innodb-develop/dictionary/SYS_TABLES.sql
mysql> source /root/undrop-for-innodb-develop/dictionary/SYS_INDEXES.sql
mysql> source /root/undrop-for-innodb-develop/dictionary/SYS_FIELDS.sql
mysql> source /root/undrop-for-innodb-develop/dictionary/SYS_COLUMNS.sql
#执行前面生成的LOAD DATA语句导入恢复的记录
shell> mysql -h127.0.0.1 -uroot -p dictionary </root/undrop-for-innodb-develop/dumps/default/SYS_TABLES.sql
shell> mysql -h127.0.0.1 -uroot -p dictionary </root/undrop-for-innodb-develop/dumps/default/SYS_COLUMNS.sql
shell> mysql -h127.0.0.1 -uroot -p dictionary </root/undrop-for-innodb-develop/dumps/default/SYS_INDEXES.sql
shell> mysql -h127.0.0.1 -uroot -p dictionary </root/undrop-for-innodb-develop/dumps/default/SYS_FIELDS.sql
到这里工作已经基本完成,因为恢复出来的元数据信息,我们已经可以在本地实例的表中查看了。接下来就是最后一步,使用sys_parser读取表中的元数据记录,并生成DDL语句。
不过这里sys_parser需要单独编译,
shell> make sys_parser
/opt/mysql/bin/mysql_config
cc -o sys_parser sys_parser.c `mysql_config --cflags` `mysql_config --libs`
最好联合mysql的安装路径进行编译,如果已经编译完成并出现如下报错的话,可以修改ld配置解决
[root@mydocker-test1 undrop-for-innodb]# ./sys_parser -h127.0.0.1 -uroot -p**** -d dictionary test_1/t1
./sys_parser: error while loading shared libraries: libmysqlclient.so.20: cannot open shared object file: No such file or directory
以上错误解析方法:
[root@node232 undrop-for-innodb-develop]# ldconfig -v | grep mysql
/opt/mysql/lib:
/usr/lib64/mysql:
libmysqlclient_r.so.16 -> libmysqlclient_r.so.16.0.0
libmysqlclient.so.16 -> libmysqlclient.so.16.0.0
[root@node232 undrop-for-innodb-develop]# find / -name 'libmysqlclient.so.20'
/usr/local/mysql-5.7.9-linux-glibc2.5-x86_64/lib/libmysqlclient.so.20
/usr/local/mysql-5.7.18-linux-glibc2.5-x86_64/lib/libmysqlclient.so.20
[root@node232 undrop-for-innodb-develop]#cp /usr/local/mysql-5.7.18-linux-glibc2.5-x86_64/lib/libmysqlclient.so.20 /usr/lib64/mysql/
[root@node232 undrop-for-innodb-develop]# ldconfig
[root@node232 undrop-for-innodb-develop]# ldconfig -v | grep mysql
/opt/mysql/lib:
/usr/lib64/mysql:
libmysqlclient.so.20 -> libmysqlclient.so.20
libmysqlclient_r.so.16 -> libmysqlclient_r.so.16.0.0
libmysqlclient.so.16 -> libmysqlclient.so.16.0.0
执行sys_parser,需要注意的是,用作表结构恢复的实例端口必须为3306,这里没有提供端口选项。
[root@mydocker-test1 undrop-for-innodb]# ./sys_parser -h127.0.0.1 -uroot -p*** -d dictionary test_1/t1
CREATE TABLE `t1`(
`id` INT NOT NULL,
`name` VARCHAR(20) CHARACTER SET 'utf8' COLLATE 'utf8_general_ci',
`age` INT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
大功告成,但是好像有一点不对?
是的,对比前面环境准备中原始的表结构,这里少了一些信息,就是二级索引。undrop-for-innodb的表结构恢复功能,只会恢复表的主干信息,不包含自增、二级索引以及外键等信息。但是这些信息我们可以通过字典数据获取。
这里我们首先通过SYS_INDEXES表找到该表的索引信息,发现除了primary key之外还有一个二级索引name,
root@localhost[dictionary]>select * from SYS_TABLES where NAME='test/t1'
-> ;
+---------+-----+--------+------+--------+---------+--------------+-------+
| NAME | ID | N_COLS | TYPE | MIX_ID | MIX_LEN | CLUSTER_NAME | SPACE |
+---------+-----+--------+------+--------+---------+--------------+-------+
| test/t1 | 205 | 3 | 33 | 0 | 80 | | 286 |
+---------+-----+--------+------+--------+---------+--------------+-------+
1 row in set (0.00 sec)
通过表的ID来查找索引的ID:
root@localhost[dictionary]>select * from SYS_INDEXES where TABLE_ID=205;
+----------+-----+---------+----------+------+-------+------------+
| TABLE_ID | ID | NAME | N_FIELDS | TYPE | SPACE | PAGE_NO |
+----------+-----+---------+----------+------+-------+------------+
| 205 | 265 | PRIMARY | 1 | 3 | 286 | 4294967295 |
| 205 | 266 | name | 1 | 0 | 286 | 4294967295 |
+----------+-----+---------+----------+------+-------+------------+
2 rows in set (0.00 sec)
然后通过SYS_FIELDS表查找这个索引的字段定义。
root@localhost[dictionary]>select * from SYS_FIELDS where index_id=266;
+----------+-----+----------+
| INDEX_ID | POS | COL_NAME |
+----------+-----+----------+
| 266 | 0 | name |
+----------+-----+----------+
1 row in set (0.00 sec)
然后我们就可以通过alter table语句恢复二级索引了。索引名为'name',并且定义中只有name这一个字段
mysql> alter table t1 add key `name`(name);
其他的表结构信息也可以通过类似方式进行手工恢复。
结语
undrop-for-innodb的更多相关文章
- undrop for innodb c_parser 不完美之处
今天发现c_parser导出数据是会丢掉某些行,给过调试发现是他处理utf8编码时计算有误,目前还没有发现自动解决总是的方法,只会手动改代码来解决. 下一步计划把c_parser移植到windows下 ...
- undrop for innodb c_parser 源码分析
一,主函数功能: 1,分析命令行参数,保存在全局变量中; 2,打开文件,加载表定义sql,调用分析函数开始处理; 3,打印导入数据的sql语句; 二,文件处理函数,void process_ibfil ...
- 记一次揪心的MySQL数据恢复过程
https://blog.csdn.net/poxiaonie/article/details/78304699 === 先说下背景,公司其中一个项目所有服务都部署在客户的机房内,机房较小,没有UPS ...
- MySQL Flashback 工具介绍
MySQL Flashback 工具介绍 DML Flashback 独立工具,通过伪装成slave拉取binlog来进行处理 MyFlash 「大众点点评」 binlog2sql 「大众点评(上海) ...
- MySQL数据库和InnoDB存储引擎文件
参数文件 当MySQL示例启动时,数据库会先去读一个配置参数文件,用来寻找数据库的各种文件所在位置以及指定某些初始化参数,这些参数通常定义了某种内存结构有多大等.在默认情况下,MySQL实例会按照一定 ...
- InnoDB关键特性学习笔记
插入缓存 Insert Buffer Insert Buffer是InnoDB存储引擎关键特性中最令人激动与兴奋的一个功能.不过这个名字可能会让人认为插入缓冲是缓冲池中的一个组成部分.其实不然,Inn ...
- InnoDB体系结构学习笔记
后台线程 Master Thread 核心的后台线程,主要负责将缓冲池的数据异步刷新到磁盘,保证数据的一致性,包括(脏页的刷新).合并插入缓冲.(UNDO页的回收)等 IO Thread 4个writ ...
- InnoDB:Lock & Transaction
InnoDB 是一个支持事务的Engine,要保证事务ACID,必然会用到Lock.就像在Java编程一下,要保证数据的线程安全性,必然会用到Lock.了解Lock,Transaction可以帮助sq ...
- innodb 自增列重复值问题
1 innodb 自增列出现重复值的问题 先从问题入手,重现下这个bug use test; drop table t1; create table t1(id int auto_increment, ...
- MySql - InnoDB - 事务 , Php版
(出处:http://www.cnblogs.com/linguanh/) 1,前序 由于要重构APP(社交类) 服务端接口的部分代码,故接触到了 innoDB,以及事务这个词,下面主要是以例子的形式 ...
随机推荐
- 90%的Java开发人员都会犯的5个错误
前言 作为一名java开发程序员,不知道大家有没有遇到过一些匪夷所思的bug.这些错误通常需要您几个小时才能解决.当你找到它们的时候,你可能会默默地骂自己是个傻瓜.是的,这些可笑的bug基本上都是你忽 ...
- Python3套接字(socket)通讯(TCP)
最近写了一个工程,用作运维的,所以研究了一下Python的TCP通讯(服务器挂一个脚本,电脑挂一个脚本,就可以通过此通讯进行编码加密后传输取回想要的内容) 服务端: from socket impor ...
- [机器学习] t-SNE聚类算法实践指南
转载于比PCA降维更高级--(R/Python)t-SNE聚类算法实践指南-阿里云开发者社区 作者介绍:Saurabh.jaju2 Saurabh是一名数据科学家和软件工程师,熟练分析各种数据集 ...
- [python] 基于matplotlib实现树形图的绘制
树形图Tree diagram (代码下载) 本文旨在描述如何使用Python实现基本的树形图.要实现这样的树形图,首先需要有一个数值矩阵.每一行代表一个实体(这里是一辆汽车).每列都是描述汽车的变量 ...
- UOJ33 [UR#2] 树上 GCD
UOJ33 [UR#2] 树上 GCD 简要题意: 给定一棵有根树,对于每个 \(i \in [1,n)\),求出下式的值: \[Ans[i] = \sum_{u<v} \gcd({\rm{di ...
- 从开发属于你自己的第一个 Python 库,做一名真正的程序员「双语版」
你好,我是悦创.之前我在 CSDN 编写了一篇开发 Python 库的教程,有人加我提问到的一些问题,我来更新一下这篇文章:https://blog.csdn.net/qq_33254766/arti ...
- Java进阶篇——设计模式
设计模式 一.代理模式 使用代理类对真实对象进行代理,包括真实对象方法的调用.功能的扩展等.访问的时候也只能访问到代理对象,既保护了真实对象同时可以在原始对象上进行扩展.类似于中介在卖家和买家之间的角 ...
- P3934 [Ynoi2016] 炸脖龙 I
题面 给一个长为 \(n\) 的序列,\(m\) 次操作,每次操作: 1.区间 \([l,r]\) 加 \(x\) 2.对于区间 \([l,r]\),查询: \[a[l]^{a[l+1]^{a[l+2 ...
- 可持久化杀手——rope学习笔记
概述 std::rope,内部一说是可持久化平衡树,一说是块状链表. 它可以实现很多可持久化数组问题. 基本使用 #include<bits/extc++.h> using namespa ...
- 【ASP.NET Core】按用户角色授权
上次老周和大伙伴们分享了有关按用户Level授权的技巧,本文咱们聊聊以用户角色来授权的事. 按用户角色授权其实更好弄,毕竟这个功能是内部集成的,多数场景下我们不需要扩展,不用自己写处理代码.从功能语义 ...