Percona Data Recovery Tool 单表恢复

Percona Data Recovery Tool 单表恢复Percona Data Recovery Tool 单表恢复

大家好,又见面了,我是你们的朋友全栈君。

转载自:https://www.cnblogs.com/gomysql/p/3586822.html

 

前几天写过update或者delete忘加where条件的数据恢复。今天介绍一款开源的MySQL数据库InnoDB数据恢复工具:innodb-tools,它通过从原始数据文件中提取表的行记录,实现从丢失的或者被毁坏的MySQL表中恢复数据。例如,当你不小心执行DROP TABLE、TRUNCATE TABLE之后,可以通过以下方式恢复数据。

在介绍innodb-tools工具进行数据恢复之前,首先明确以下几点:

1、这个工具只能对InnoDB/XtraDB表有效,而无法恢复MyISAM表

2、这个工具是以保存的MySQL数据文件进行恢复的,而不用MySQL Server运行。

3、不能保证数据总一定可被恢复。例如,被重写的数据不能被恢复,这种情况下可能需要针对系统或物理的方式来恢复,不属于本工具的范畴。

4、恢复的最好时机是当你发现数据丢失时,尽快备份MySQL数据文件。

5、使用这个工具需要手动做一些工作,并不是全自动完成的。

6、恢复过程依赖于你对丢失数据的了解程度,在恢复过程中可能需要在不同版本的数据之间做出选择。那么如果你越了解自己的数据,恢复的可能性就越大。

接下来,下面通过一个例子来介绍如何通过这个工具进行恢复。

1. 前提条件

首先,需要理解的是innodb-tools工具不是通过连接到在线的database进行数据恢复,而是通过离线拷贝数据的方式进行的。注意:不要在MySQL运行的时候,直接拷贝InnoDB文件,这样是不安全的,会影响数据恢复过程。

为了完成数据恢复,必须知道将要被恢复的表结构(列名、数据类型)。最简单的方式就是SHOW CREATE TABLE,当然后续会介绍几种可替代的方式。因此,如果有一个MySQL server作为备份,即使数据是很早的甚至表中没有记录,可以有助于使用innodb-tools工具进行恢复。不过这个不是必须的。

2.简单例子

复制代码
mysql> use book;
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 count(*) from million_words;
+----------+
| count(*) |
+----------+
|  1000000 |
+----------+
1 row in set (0.13 sec)

mysql> truncate million_words;
Query OK, 0 rows affected (0.23 sec)

mysql> 
复制代码

3.构建工具

1、下载解压innodb-tools工具源码:

安装依赖,否则抛出如下错误:

/usr/bin/ld: cannot find -lrt
collect2: ld returned 1 exit 
status
make: *** [page_parser] Error 1

[root@localhost ~]# yum install glibc-static -y

wget https://launchpad.net/percona-data-recovery-tool-for-innodb/trunk/release-0.5/+download/percona-data-recovery-tool-for-innodb-0.5.tar.gz
tar -xvf percona-data-recovery-tool-for-innodb-0.5.tar.gz -C /usr/local/

cd /usr/local
ln -s percona-data-recovery-tool-for-innodb-0.5 percona-data-recovery-tool

2、进入解压后根目录下的mysql-source目录,运行配置命令(注:不运行make命令):

[root@localhost mysql-source]# cd /usr/local/percona-data-recovery-tool/mysql-source/
[root@localhost mysql-source]# ./configure 

3、完成配置步骤后,回到解压后的根目录,运行make命令,编译生成page_parserconstraints_parser工具

[root@localhost mysql-source]# cd ..
[root@localhost percona-data-recovery-tool]# make

page_parser工具将根据InnoDB的底层实现原理,解析表的页和行结构。constraints_parser工具暂时不使用,后续还需要在定义表结构之后,重新编译生成它。

4. 提取需要的页

InnoDB页的默认大小是16K,每个页属于一个特定表中的一个特定的index。page_parser工具通过读取数据文件,根据页头中的index ID,拷贝每个页到一个单独的文件中。

如果启用了innodb_file_per_table=1,也就是独立表空间文件,那么将无法完全恢复数据,本人也已经测试过,官方文档也没有提到启用独立表空间是否可以成功,在官方文档中,是设置innodb_file_per_table=0。

参考资料如下:

http://www.percona.com/docs/wiki/innodb-data-recovery-tool:mysql-data-recovery:example_data_loss_scenario

4.1 切分页

运行page_parser工具进行切分:

如果MySQL是5.0之前的版本,InnoDB采取的是REDUNDANT格式,运行以下命令:

./page_parser -4 -f /path/to/ibdata1

如果MySQL是5.0以后的版本,InnoDB采取的是COMPACT格式,运行以下命令:

./page_parser -5 -f /path/to/ibdata1

运行后,page_parser工具会创建一个pages-<TIMESTAMP>的目录,其中TIMESTAMP是UNIX系统时间戳。在这个目录下,为每个index ID,以页的index ID创建一个子目录。例如:

复制代码
[root@localhost percona-data-recovery-tool]# ./page_parser -5 -f /data/mysql/ibdata1 
Opening file: /data/mysql/ibdata1:
2053            ID of device containing file
130915          inode number
33200           protection
1               number of hard links
500             user ID of owner
501             group ID of owner
0               device ID (if special file)
371195904               total size, in bytes
4096            blocksize for filesystem I/O
725000          number of blocks allocated
1394179630      time of last access
1394179669      time of last modification
1394179669      time of last status change
371195904       Size to process in bytes
104857600       Disk cache size in bytes
1.00% done. 2014-03-07 16:31:45 ETA(in 00:04 hours). Processing speed: 1237320 B/sec
2.00% done. 2014-03-07 16:28:27 ETA(in 00:01 hours). Processing speed: 3726376 B/sec
8.80% done. 2014-03-07 16:27:04 ETA(in 00:00 hours). Processing speed: 25231360 B/sec
9.80% done. 2014-03-07 16:28:22 ETA(in 00:01 hours). Processing speed: 3719168 B/sec
20.80% done. 2014-03-07 16:27:00 ETA(in 00:00 hours). Processing speed: 40819863 B/sec
28.25% done. 2014-03-07 16:27:35 ETA(in 00:00 hours). Processing speed: 6912218 B/sec
40.96% done. 2014-03-07 16:27:02 ETA(in 00:00 hours). Processing speed: 47167140 B/sec
51.43% done. 2014-03-07 16:27:03 ETA(in 00:00 hours). Processing speed: 38881628 B/sec
56.49% done. 2014-03-07 16:27:37 ETA(in 00:00 hours). Processing speed: 4698112 B/sec
76.23% done. 2014-03-07 16:27:05 ETA(in 00:00 hours). Processing speed: 73252864 B/sec
83.23% done. 2014-03-07 16:27:07 ETA(in 00:00 hours). Processing speed: 26001408 B/sec
84.74% done. 2014-03-07 16:28:00 ETA(in 00:00 hours). Processing speed: 1117388 B/sec
90.79% done. 2014-03-07 16:27:12 ETA(in 00:00 hours). Processing speed: 22452811 B/sec
99.00% done. 2014-03-07 16:27:12 ETA(in 00:00 hours). Processing speed: 30479427 B/sec
[root@localhost percona-data-recovery-tool]# 
复制代码

[root@localhost percona-data-recovery-tool]# ll pages-1394180806/FIL_PAGE_INDEX/0-1
total 16
-rw-r--r-- 1 root root 16384 Mar  7 16:26 1-00000008.page

4.2 选择需要的Index ID

一般来说,我们需要根据表的主键(PRIMARY index)进行恢复,主键中包含了所有的行。以下是一些可以实现的步骤:

如果数据库仍处于运行状态,并且表没有被drop掉,那么可以启动InnoDB Tablespace Monitor,输出所有表和indexes,index IDs到MySQL server的错误日志文件。创建innodb_table_monitor表用于收集innodb存储引擎表及其索引的存储方式:

mysql> CREATE TABLE test.innodb_table_monitor (id int) ENGINE=InnoDB;
Query OK, 0 rows affected (0.31 sec)

mysql> 

如果innodb_table_monitor已经存在,drop表然后重新create表。等MySQL错误日志输出后,可以drop掉这张表以停止打印输出更多的监控。一个输出的例子如下:

复制代码
TABLE: name book/million_words, id 239, flags 1, columns 5, indexes 2, appr.rows 0
  COLUMNS: id: DATA_INT DATA_UNSIGNED DATA_BINARY_TYPE DATA_NOT_NULL len 4; word: DATA_VARMYSQL DATA_NOT_NULL len 150; DB_ROW_ID: DATA_SYS prtype 256 len 6; DB_TRX_ID: DATA_SYS prtype 257 len 6; DB_ROLL_PTR: DAT
A_SYS prtype 258 len 7;
  INDEX: name PRIMARY, id 374, fields 1/4, uniq 1, type 3
   root page 16419, appr.key vals 0, leaf pages 1, size pages 1
   FIELDS:  id DB_TRX_ID DB_ROLL_PTR word
  INDEX: name word, id 375, fields 1/2, uniq 1, type 2
   root page 16420, appr.key vals 0, leaf pages 1, size pages 1
   FIELDS:  word id
复制代码

这里,我们恢复的是sakila库下的customer表,从上面可以获取其主键信息:

INDEX: name PRIMARY, id 374, fields 1/4, uniq 1, type 3

Index ID是0 374,因此我们需要恢复的InnoDB页位于0-374子目录下。

备注:参考文档原文中之描述了以上这种获取表的index ID的方法,本文在实际操作中,采取了更简单的一种方式,即直接恢复page_parser生成的所有InnoDB页。实践证明这种方法也是可行的.

5. 生成表定义

步骤4中,我们已经找到了需要的数据,接下来需要找到表结构,创建表定义,将其编译到constraints_parser中,然后使用这个工具从InnoDB页中提取表中的行。

表定义包含了表中的列、列顺序、数据类型。如果MySQL server仍处于运行且表未被drop掉,那么简单实用SHOW CREATE TABLE就可以收集到这些信息。接下来将使用这些表结构信息来创建一个C结构体标识的表定义,然后编译到constraints_parser工具。C结构体的定义存放在include/table_defs.h中。

最简单的方式是create_defs.pl Perl 脚本,连接到MySQL server,读取SHOW CREATE TABLE的结果,输出生成的表定义到标准输出。下面是个例子,其中直接将结果重定向到了include/table_defs.h中:

[root@localhost percona-data-recovery-tool]# ./create_defs.pl --host=127.0.0.1 --user=root --password=yayun --db=book --table=million_words  > include/table_defs.h

下面是生成的表定义:

复制代码
#ifndef table_defs_h
#define table_defs_h

// Table definitions
table_def_t table_definitions[] = {
        {
                name: "million_words",
                {
                        { /* int(10) unsigned */
                                name: "id",
                                type: FT_UINT,
                                fixed_length: 4,

                                has_limits: FALSE,
                                limits: {
                                        can_be_null: FALSE,
                                        uint_min_val: 0,
                                        uint_max_val: 4294967295ULL
                                },

                                can_be_null: FALSE
                        },
                        { /*  */
                                name: "DB_TRX_ID",
                                type: FT_INTERNAL,
                                fixed_length: 6,

                                can_be_null: FALSE
                        },
                        { /*  */
                                name: "DB_ROLL_PTR",
                                type: FT_INTERNAL,
                                fixed_length: 7,

                                can_be_null: FALSE
                        },
                        { /* varchar(50) */
                                name: "word",
                                type: FT_CHAR,
                                min_length: 0,
                                max_length: 150,

                                has_limits: FALSE,
                                limits: {
                                        can_be_null: FALSE,
                                        char_min_len: 0,
                                        char_max_len: 150,
                                        char_ascii_only: TRUE
                                },

                                can_be_null: FALSE
                        },
                        { type: FT_NONE }
                }
        },
};

#endif
复制代码

如果需要,可以根据需要编辑修改include/table_defs.h;然后根据include/table_defs.h,重新编译constraints_parser工具:

复制代码
[root@localhost percona-data-recovery-tool]# make
gcc -DHAVE_OFFSET64_T -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE=1 -D_LARGEFILE_SOURCE=1 -Wall -O3 -g -I include -I mysql-source/include -I mysql-source/innobase/include -c tables_dict.c -o lib/tables_dict.o
gcc -DHAVE_OFFSET64_T -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE=1 -D_LARGEFILE_SOURCE=1 -Wall -O3 -g -I include -I mysql-source/include -I mysql-source/innobase/include -c print_data.c -o lib/print_data.o 
gcc -DHAVE_OFFSET64_T -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE=1 -D_LARGEFILE_SOURCE=1 -Wall -O3 -g -I include -I mysql-source/include -I mysql-source/innobase/include -c check_data.c -o lib/check_data.o
gcc -DHAVE_OFFSET64_T -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE=1 -D_LARGEFILE_SOURCE=1 -Wall -O3 -g -I include -I mysql-source/include -I mysql-source/innobase/include -o constraints_parser constraints_parser.c lib/tables_dict.o lib/print_data.o lib/check_data.o lib/libut.a lib/libmystrings.a
gcc -DHAVE_OFFSET64_T -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE=1 -D_LARGEFILE_SOURCE=1 -Wall -O3 -g -I include -I mysql-source/include -I mysql-source/innobase/include -static -lrt -o page_parser page_parser.c lib/tables_dict.o lib/libut.a 
[root@localhost percona-data-recovery-tool]# 
复制代码

6. 从页中提取行记录

6.1 合并页到一个文件

前面已经提到,我们需要恢复的index ID 0 374,包含数据的页位于pages-1394180806/FIL_PAGE_INDEX/0-374/ 目录。

复制代码
[root@localhost percona-data-recovery-tool]# cd pages-1394180806/FIL_PAGE_INDEX/0-374/
[root@localhost 0-374]# ll
total 36176
-rw-r--r-- 1 root root 16384 Mar  7 16:26 104-00000306.page
-rw-r--r-- 1 root root 16384 Mar  7 16:26 106-00000309.page
-rw-r--r-- 1 root root 16384 Mar  7 16:26 107-00000310.page
-rw-r--r-- 1 root root 16384 Mar  7 16:26 109-00000312.page
-rw-r--r-- 1 root root 16384 Mar  7 16:26 110-00000314.page
-rw-r--r-- 1 root root 16384 Mar  7 16:26 11-00001416.page
-rw-r--r-- 1 root root 16384 Mar  7 16:26 112-00000316.page
-rw-r--r-- 1 root root 16384 Mar  7 16:27 11732-00016419.page
-rw-r--r-- 1 root root 16384 Mar  7 16:26 13-00016419.page
-rw-r--r-- 1 root root 16384 Mar  7 16:26 16-00003437.page
................................
................................
复制代码

输入以下命令进行合并页:

[root@localhost percona-data-recovery-tool]# find pages-1394180806/FIL_PAGE_INDEX/0-374/ -type f -name '*.page' | sort -n | xargs cat > pages-1394180806/FIL_PAGE_INDEX/0-374/customer_pages_concatenated
[root@localhost percona-data-recovery-tool]#

生成的结果文件:pages-1394180806/FIL_PAGE_INDEX/0-374/customer_pages_concatenated,将作为constraints_parser工具的输入。

6.2 运行constraints_parser工具

下面到恢复数据最核心的步骤——运行constraints_parser工具以提取行记录。和page_parser工具一样,需要通过-5或-4参数指定InnoDB页格式(COMPACT/REDUNDANT),-f指定输入文件。

回到例子中,我们可以这样运行constraints_parser工具

我们可以这样运行constraints_parser工具(下面的命令是恢复一个单一的页,也可以直接恢复经过6.1步骤合并所有页之后的文件):

./constraints_parser -5 -f pages-1394180806/FIL_PAGE_INDEX/0-374/76-00003397.page

会输出恢复数据相关语句:

LOAD DATA INFILE '/usr/local/percona-data-recovery-tool/dumps/default/million_words' REPLACE INTO TABLE `million_words` FIELDS TERMINATED BY '\t' OPTIONALLY ENCLOSED BY '"' LINES STARTING BY 'million_words\t' (id, word);

既然这样,那么我们就创建dumps/default/文件夹

[root@localhost percona-data-recovery-tool]# pwd
/usr/local/percona-data-recovery-tool
[root@localhost percona-data-recovery-tool]# mkdir dumps/default -p
[root@localhost percona-data-recovery-tool]# 

恢复全部页的数据到/dumps/default/million_words

[root@localhost percona-data-recovery-tool]# ./constraints_parser -5 -f pages-1394180806/FIL_PAGE_INDEX/0-374/customer_pages_concatenated >> dumps/default/million_words

输出提示如下,省略了一些内容:

复制代码
95.98% done
96.42% done
96.86% done
97.30% done
97.74% done
98.19% done
98.63% done
99.07% done
99.51% done
99.96% done
LOAD DATA INFILE '/usr/local/percona-data-recovery-tool/dumps/default/million_words' REPLACE INTO TABLE `million_words` FIELDS TERMINATED BY '\t' OPTIONALLY ENCLOSED BY '"' LINES STARTING BY 'million_words\t' (id, word);
复制代码

7. 导入数据到数据库中

复制代码
mysql> use book
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> LOAD DATA INFILE '/usr/local/percona-data-recovery-tool/dumps/default/million_words' REPLACE INTO TABLE `million_words` FIELDS TERMINATED BY '\t' OPTIONALLY ENCLOSED BY '"' LINES STARTING BY 'million_words\t' (id, word);
Query OK, 1062600 rows affected (14.99 sec)
Records: 1031300  Deleted: 31300  Skipped: 0  Warnings: 0

mysql> select count(*) from million_words;
+----------+
| count(*) |
+----------+
|  1000000 |
+----------+
1 row in set (0.20 sec)

mysql> 
复制代码

可以看见数据已经恢复回来,我测试的MySQL版本如下:

复制代码
mysql> select version();           
+-------------+
| version()   |
+-------------+
| 5.5.25a-log |
+-------------+
1 row in set (0.00 sec)

mysql> show variables like 'innodb_file_per_table';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_file_per_table | OFF   |
+-----------------------+-------+
1 row in set (0.00 sec)

mysql> 
复制代码

希望各位小伙伴永远不需要用到此方法,备份才是王道啊!

参考资料如下:

http://www.percona.com/docs/wiki/innodb-data-recovery-tool:mysql-data-recovery:start

http://hidba.org/?p=852

转载于:https://www.cnblogs.com/liang545621/p/9450906.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/107370.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 列举数据挖掘领域的十大挑战问题

    列举数据挖掘领域的十大挑战问题

    2021年8月23日
    79
  • 简单的说下nginx和apache的区别~~~[通俗易懂]

    简单的说下nginx和apache的区别~~~[通俗易懂]浅谈nginx和apache的优缺点~~~一、分别介绍nginx和apache1.nginx2.apache二、apache相对于nginx的优缺点1.优点2.缺点总结一、分别介绍nginx和apache1.nginx什么是nginx:Nginx是一个高性能的HTTP和反向代理服务器,同时还是IMAP/POP3/SMTP代理服务器,该程序由俄罗斯Rambler.ru站点开发,Nginx因为性能稳定、低系统资源消耗而闻名,近几年Nginx在国内已经成炙热化状态,比如像腾讯、网易、51CTO、迅雷、当当

    2022年5月29日
    35
  • NSGA2 Python实现

    NSGA2 Python实现#importingnecessarymodulesimportmathimportrandomimportmatplotlib.pyplotasPlt#FirstFunctiontooptimizedeffunction1(x1,x2):value=-x1*2+x2returnvalue#SecondFunctiontooptimizedeffunction2(x1,x2):value=-x1*5*x2

    2022年5月12日
    43
  • java 常量池和运行时常量池_常量池在jvm的哪个部分

    java 常量池和运行时常量池_常量池在jvm的哪个部分前言一直在《深入理解JVM》对常量池只有一个浅薄的了解,之前也遇到过这种题目,今天还是要挑出来进行一次全方位的了解。常量池分类常量池大体可以分为:静态常量池,运行时常量池。静态常量池存在于class文件中,比如经常使用的javap-verbose中,常量池总是在最前面把?运行时常量池呢,就是在class文件被加载进了内存之后,常量池保存在了方法区中,通常说的常量池值的…

    2025年10月17日
    5
  • AWK介绍[通俗易懂]

    AWK介绍[通俗易懂]0.awk有3个不同版本:awk、nawk和gawk,未作特别说明,一般指gawk。1.awk语言的最基本功能是在文件或字符串中基于指定规则来分解抽取信息,也可以基于指定的规则来输出数据。完整的awk脚本通常用来格式化文本文件中的信息。2.三种方式调用awk1)awk[opion]’awk_script’input_file1[input_file2…]

    2022年7月27日
    11
  • SQL使用模糊查询like的优化

    SQL使用模糊查询like的优化 SQL使用模糊查询like’%ABC’和like’%ABC%’的优化 &#1…

    2022年5月11日
    39

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号