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


相关推荐

  • windows7下预览psd

    windows7下预览psdPhotoshopcs5安装完后电脑里PSD格式的文件无法显示缩略图,想看原图必须打开软件才能知道图片的内容,安装此补丁之后不用打开软件,在文件夹里就可以看到PSD格式图片的内容,挺方便的,本人亲自测试可以在win7下使用。使用方法:将"psicon.dll"文件拷备到系统system32目录下,不然的话重启之后又看不到缩略图了。步骤一:解压到任何文件夹;步骤二:将psicon.dll拷备到…

    2022年6月11日
    32
  • macOS tcping 检测IP端口

    macOS tcping 检测IP端口检测IP端口1,安装tcping2,检测IP端口是否被屏蔽1,安装tcping$brewsearchtcping==>Formulaetcping$brewinstalltcpingError:Thefollowingdirectoriesarenotwritablebyyouruser:/usr/local/share/man/man8Yo…

    2022年6月23日
    94
  • HibernateTemplate的常用方法「建议收藏」

    HibernateTemplate的常用方法「建议收藏」HibernateTemplate提供非常多的常用方法来完成基本的操作,比如通常的增加、删除、修改、查询等操作,Spring2.0更增加对命名SQL查询的支持,也增加对分页的支持。大部分情况下,使用Hibernate的常规用法,就可完成大多数DAO对象的CRUD操作。 下面是HibernateTemplate的常用方法简介:     voiddelete(Objecte…

    2022年6月22日
    33
  • js中判断对象是否为空

    js中判断对象是否为空1.es6中可以使用Object.keys(obj)vardata={};vararr=Object.keys(data);alert(arr.length==0);//true为空,false不为空2.将json对象转化为json字符串,再判断该字符串是否为"{}"vardata={};varb=(JSON.stringify(data)==…

    2022年6月14日
    35
  • python菜鸟踩坑系列-pika版本带来的问题

    python菜鸟踩坑系列-pika版本带来的问题

    2021年5月16日
    210
  • SpringBoot整合Quartz定时任务(持久化到数据库)

    背景最近在做项目,项目中有个需求:需要使用定时任务,这个定时任务需要即时生效。查看Quartz官网之后发现:Quartz提供两种基本作业存储类型:RAMJobStore:RAM也就是内存,默认情况下Quartz会将任务调度存在内存中,这种方式性能是最好的,因为内存的速度是最快的。不好的地方就是数据缺乏持久性,但程序崩溃或者重新发布的时候,所有运行信息都会丢失JDBC作业存储:存到数据库…

    2022年4月9日
    80

发表回复

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

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