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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Lua使用心得(2)

    在lua脚本调用中,如果我们碰到一种不好的脚本,例如:while1dodoend那我们的程序主线程也会被阻塞住。那我们如何防止这种问题呢?下面就给出一个解决的办法。首先为了不阻塞主线程,

    2021年12月25日
    54
  • IIC通信协议总结[通俗易懂]

    IIC通信协议总结[通俗易懂](1)概述I2C(Inter-IntegratedCircuit BUS)集成电路总线,该总线由NXP(原PHILIPS)公司设计,多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。经常IIC和SPI接口被认为指定是一种硬件设备,但其实这样的说法是不尽准确的,严格的说他们都是人们所定义的软硬结合体,分为物理层(四线结构)和协议层(主机,从

    2022年4月28日
    59
  • hdu1024—最大m段不重合子序列和:策略优化

    hdu1024—最大m段不重合子序列和:策略优化

    2021年8月7日
    59
  • Java学习之集合篇

    Java学习之集合篇0x00前言上篇文章的常用类,其实就已经讲到了,这个ArrayList集合,但是ArrayList只是集合中的其中一种,那这篇文章就来讲讲关于集合的一些对象。0x01集合

    2021年12月12日
    53
  • 几种字符乱码

    几种字符乱码其他编码转成iso8859-1出现乱码?(问号):   原因:是因为iso8859-*的处理逻辑,对不存在的的码值直接解析为?号(0x3F)  演示://控制台设置为iso8859-1,输出一个左手图标”☜”,控制台显示乱码System.out.println(‘\u261c’);   解决:   处理好不同编码,iso是西欧用的比较多的编码,如果

    2022年6月7日
    35
  • 佳能A720IS 使用技巧集

    佳能A720IS 使用技巧集买佳能A720IS好长时间了,一直都是以auto模式进行拍摄,今天忽然想学习一下A720IS的使用技巧,于是就在网上搜到了下面的技巧,在这里发出来,和朋友们共享一下.1、点测光怎么用?点测光是从P档

    2022年7月3日
    39

发表回复

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

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