mysql drop table 恢复_DROP TABLE 恢复【一】

本文详细介绍了如何在MySQL中误删InnoDB表后,通过TwinDB恢复工具包从ibdata1文件恢复数据的过程。首先停止mysqld进程,使用stream_parser解析并保存表页,然后通过c_parser找到表标识符和主键ID,最后将数据导入数据库,成功恢复被DROP的表。
摘要由CSDN通过智能技术生成

当DROP TABLE指令敲下的时候,你很爽,你有考虑过后果么?如果该表真的没用,你DROP到无所谓,如果还有用的,这时你肯定吓惨了吧,如果你有备份,那么恭喜你,逃过一劫,如果没有备份呢?这时就该绝望了?NO! 如果你的表是innodb表,那么还有希望挽救,如果是myisam表,那么真的没救了。前面文章介绍了 Recover InnoDB dictionary,这是恢复数据的前提。恢复innodb字典信息使用的是TwinDB recovery toolkit,我们恢复数据也是使用该工具。下面的案例是基于innodb_file_per_table=OFF的前提下,即使用共享表空间,所有的信息都保存在ibdata1中。使用独立表空间DROP TABLE后数据恢复将在后面的文章介绍。

错误的操作--删除表

用到的示例数据库还是sakila,关于下载地址前面的文章有地址。将模拟把sakila库中的actor表删除后进行恢复。

root@localhost : sakila 21:34:11> SELECT * FROM actor LIMIT 10;+----------+------------+--------------+---------------------+

| actor_id | first_name | last_name | last_update |

+----------+------------+--------------+---------------------+

| 1 | PENELOPE | GUINESS | 2006-02-15 04:34:33 |

| 2 | NICK | WAHLBERG | 2006-02-15 04:34:33 |

| 3 | ED | CHASE | 2006-02-15 04:34:33 |

| 4 | JENNIFER | DAVIS | 2006-02-15 04:34:33 |

| 5 | JOHNNY | LOLLOBRIGIDA | 2006-02-15 04:34:33 |

| 6 | BETTE | NICHOLSON | 2006-02-15 04:34:33 |

| 7 | GRACE | MOSTEL | 2006-02-15 04:34:33 |

| 8 | MATTHEW | JOHANSSON | 2006-02-15 04:34:33 |

| 9 | JOE | SWANK | 2006-02-15 04:34:33 |

| 10 | CHRISTIAN | GABLE | 2006-02-15 04:34:33 |

+----------+------------+--------------+---------------------+

10 rows in set (0.01sec)

root@localhost : sakila 21:34:25>

root@localhost : sakila 21:34:25> CHECKSUM TABLEactor;+--------------+------------+

| Table | Checksum |

+--------------+------------+

| sakila.actor | 2472295518 |

+--------------+------------+

1 row in set (0.07sec)

root@localhost : sakila 21:35:30> SET foreign_key_checks=OFF;

Query OK,0 rows affected (0.00sec)

root@localhost : sakila 21:35:46> DROP TABLEactor;

Query OK,0 rows affected (0.07sec)

root@localhost : sakila 21:35:57>

从ibdata1恢复数据

现在actor表已经删除,但表中的信息仍然存与ibdata1中。该数据保持不变,直到InnoDB的重用空闲的页。我们需要尽快停止mysqld进程。

对于恢复,我们将使用TwinDB恢复工具包。看看我前面的文章Recover InnoDB dictionary。

解析innodb表空间(ibdata1)

InnoDB将所有数据存储在B +树索引。 一个表有只有一个聚集索引,所有字段存储在这里。 如果表有辅助索引,由index_id标识每个索引。

如果我们要恢复一个表,我们必须找到属于特定index_id的所有页面。

stream_parser读取InnoDB表和排序按类型和每个index_id的InnoDB的页面。

[root@mysql-server-01 undrop-for-innodb]# ./stream_parser -f /data/mysql/user_3306/data/ibdata1

Openingfile: /data/mysql/user_3306/data/ibdata1

File information:

ID of device containingfile: 2055inode number:77760163protection:100660 (regular file)

number of hard links:1user ID of owner:498group ID of owner:500device ID (if special file): 0blocksizefor filesystem I/O: 4096number of blocks allocated:53248

time of last access: 1407057329 Sun Aug 3 17:15:29 2014

time of last modification: 1407072967 Sun Aug 3 21:36:07 2014

time of last status change: 1407072967 Sun Aug 3 21:36:07 2014total size,in bytes: 27262976 (26.000MiB)

Size to process:27262976 (26.000MiB)

All workers finishedin 0sec

[root@mysql-server-01 undrop-for-innodb]#

使用stream_parser将把数据从page保存到pages-ibdata1

[root@mysql-server-01 FIL_PAGE_INDEX]# pwd

/root/undrop-for-innodb/pages-ibdata1/FIL_PAGE_INDEX

[root@mysql-server-01FIL_PAGE_INDEX]# ll

total6976

-rw-r--r-- 1 root root 32768 Aug 3 21:59 0000000000000001.page-rw-r--r-- 1 root root 49152 Aug 3 21:59 0000000000000002.page-rw-r--r-- 1 root root 49152 Aug 3 21:59 0000000000000003.page-rw-r--r-- 1 root root 49152 Aug 3 21:59 0000000000000004.page-rw-r--r-- 1 root root 32768 Aug 3 21:59 0000000000000005.page

。。。。。。。。。。。。。。。。。。。。。。。。。-rw-r--r-- 1 root root 16384 Aug 3 21:59 0000000000000011.page-rw-r--r-- 1 root root 16384 Aug 3 21:59 0000000000000012.page-rw-r--r-- 1 root root 16384 Aug 3 21:59 0000000000000013.page-rw-r--r-- 1 root root 16384 Aug 3 21:59 0000000000000053.page-rw-r--r-- 1 root root 16384 Aug 3 21:59 0000000000000054.page-rw-r--r-- 1 root root 16384 Aug 3 21:59 0000000000000055.page-rw-r--r-- 1 root root 16384 Aug 3 21:59 18446744069414584320.page

现在InnoDB表空间的每个index_id被保存在一个单独的文件。我们可以用c_parser工具从page提取记录。但是,我们需要知道哪个index_id对应表Sakila/actor。这些信息,我们可以从字典中获取:SYS_TABLES和SYS_INDEXES。

SYS_TABLES始终存储在文件index_id为1的page,ibdata1/FIL_PAGE_INDEX/0000000000000001.page 这让我们找到Sakila/actor表的标识符。如果MySQL有足够的时间来刷新到磁盘的变化再加入D选项,意思是“寻找被删除的记录“,innodb字典信息永远是冗余格式,所以我们需要指定选项-4。

[root@mysql-server-01 undrop-for-innodb]# ./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000001.page -t dictionary/SYS_TABLES.sql | grep sakila/actor000000000344 45000002B902C8 SYS_TABLES "sakila/actor" 13 4 1 0 0 "" 0

000000000344 45000002B902C8 SYS_TABLES "sakila/actor" 13 4 1 0 0 "" 0SET FOREIGN_KEY_CHECKS=0;

LOAD DATA LOCAL INFILE'/root/undrop-for-innodb/dumps/default/SYS_TABLES' REPLACE INTO TABLE `SYS_TABLES` FIELDS TERMINATED BY '\t' OPTIONALLY ENCLOSED BY '"' LINES STARTING BY 'SYS_TABLES\t'(`NAME`, `ID`, `N_COLS`, `TYPE`, `MIX_ID`, `MIX_LEN`, `CLUSTER_NAME`, `SPACE`);

[root@mysql-server-01 undrop-for-innodb]#

注意表名之后的数13。这是表标识符。这和前面的文章不谋而合,Recover InnoDB dictionary

接下来的事情,需要做的是找到actor表的的主键ID。为此,我们将从SYS_INDEXES文件0000000000000003.page获取记录(该表将包含有关index_id和表标识符信息)。 SYS_INDEXES的结构需要通过-t选项解析。

[root@mysql-server-01 undrop-for-innodb]# ./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000003.page -t dictionary/SYS_INDEXES.sql | grep 13

000000000344 45000002B90145 SYS_INDEXES 13 15 "PRIMARY" 1 3 0 4294967295

000000000344 45000002B901B7 SYS_INDEXES 13 16 "idx\_actor\_last\_name" 1 0 0 4294967295

000000000344 45000002B90145 SYS_INDEXES 13 15 "PRIMARY" 1 3 0 4294967295

000000000344 45000002B901B7 SYS_INDEXES 13 16 "idx\_actor\_last\_name" 1 0 0 4294967295

000000000344 45000002B90145 SYS_INDEXES 13 15 "PRIMARY" 1 3 0 4294967295

000000000344 45000002B901B7 SYS_INDEXES 13 16 "idx\_actor\_last\_name" 1 0 0 4294967295SET FOREIGN_KEY_CHECKS=0;

LOAD DATA LOCAL INFILE'/root/undrop-for-innodb/dumps/default/SYS_INDEXES' REPLACE INTO TABLE `SYS_INDEXES` FIELDS TERMINATED BY '\t' OPTIONALLY ENCLOSED BY '"' LINES STARTING BY 'SYS_INDEXES\t'(`TABLE_ID`, `ID`, `NAME`, `N_FIELDS`, `TYPE`, `SPACE`, `PAGE_NO`);

[root@mysql-server-01 undrop-for-innodb]#

我们可以从输出看到,PRIMARY index_id标示符是15。因此,我们的数据将从0000000000000015.page寻找。

[root@mysql-server-01 undrop-for-innodb]# ./c_parser -6f pages-ibdata1/FIL_PAGE_INDEX/0000000000000015.page -t sakila/actor.sql | head -10

-- Page id: 307, Format: COMPACT, Records list: Valid, Expected records: (200 200)

00000000032C AD000001750110 actor1 "PENELOPE" "GUINESS" "2006-02-15 04:34:33"00000000032C AD00000175011A actor2 "NICK" "WAHLBERG" "2006-02-15 04:34:33"00000000032C AD000001750124 actor3 "ED" "CHASE" "2006-02-15 04:34:33"00000000032C AD00000175012E actor4 "JENNIFER" "DAVIS" "2006-02-15 04:34:33"00000000032C AD000001750138 actor5 "JOHNNY" "LOLLOBRIGIDA" "2006-02-15 04:34:33"00000000032C AD000001750142 actor6 "BETTE" "NICHOLSON" "2006-02-15 04:34:33"00000000032C AD00000175014C actor7 "GRACE" "MOSTEL" "2006-02-15 04:34:33"00000000032C AD000001750156 actor8 "MATTHEW" "JOHANSSON" "2006-02-15 04:34:33"00000000032C AD000001750160 actor9 "JOE" "SWANK" "2006-02-15 04:34:33"[root@mysql-server-01 undrop-for-innodb]#

看见上面的输出,是不是觉得希望来了?哈哈

上面的结果正是我们想要的,我们现在把数据存贮在文件中,然后倒入,创建dump/default目录存储数据。

[root@mysql-server-01 undrop-for-innodb]# mkdir -p dumps/default

[root@mysql-server-01 undrop-for-innodb]#

[root@mysql-server-01 undrop-for-innodb]# ./c_parser -6f pages-ibdata1/FIL_PAGE_INDEX/0000000000000015.page -t sakila/actor.sql > dumps/default/actor 2> dumps/default/actor_load.sql

[root@mysql-server-01 undrop-for-innodb]#

我们看看一个文件,其实是命令加载表而已

[root@mysql-server-01 undrop-for-innodb]# cat dumps/default/actor_load.sql

SET FOREIGN_KEY_CHECKS=0;

LOAD DATA LOCAL INFILE'/root/undrop-for-innodb/dumps/default/actor' REPLACE INTO TABLE `actor` FIELDS TERMINATED BY '\t' OPTIONALLY ENCLOSED BY '"' LINES STARTING BY 'actor\t'(`actor_id`, `first_name`, `last_name`, `last_update`);

[root@mysql-server-01 undrop-for-innodb]#

将数据load回数据库中

现在将数据恢复到数据库中。但是,在导入数据以前,我们需要创建表actor(前提我们要有表结构备份,如果没有只有使用另外的工具找到表结构Percona Data Recovery Tool)看来还是需要两个工具结合使用啊。

root@localhost : sakila 23:03:50> source sakila/actor.sql

root@localhost : sakila 23:03:50> show create tableactor\G*************************** 1. row ***************************

Table: actorCreate Table: CREATE TABLE`actor` (

`actor_id`smallint(5) unsigned NOT NULLAUTO_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=utf81 row in set (0.00sec)

root@localhost : sakila 23:04:36>

现在我们导入数据,恢复actor表

[root@mysql-server-01 undrop-for-innodb]# mysql -uroot -p123456 -S /data/mysql/user_3306/mysql.sock --local-infile

Welcome to the MySQL monitor. Commands end with ; or\g.

Your MySQL connection idis 18Server version:5.5.37-logMySQL Community Server (GPL)

Copyright (c)2000, 2014, Oracle and/or its affiliates. Allrights reserved.

Oracleis a registered trademark of Oracle Corporation and/orits

affiliates. Other names may be trademarksoftheir respective

owners.

Type'help;' or '\h' for help. Type '\c' to clear the currentinput statement.

root@localhost : (none) 23:17:16> usesakilaDatabasechanged

root@localhost : sakila 23:17:19> source dumps/default/actor_load.sql;

Query OK,0 rows affected (0.00sec)

Query OK,600 rows affected (0.08sec)

Records:400 Deleted: 200 Skipped: 0 Warnings: 0root@localhost : sakila 23:17:22>

检查恢复的数据

root@localhost : sakila 23:19:00> SELECT COUNT(*) FROMactor;+----------+

| COUNT(*) |

+----------+

| 200 |

+----------+

1 row in set (0.00sec)

root@localhost : sakila 23:19:34> SELECT * FROM actor LIMIT 10;+----------+------------+--------------+---------------------+

| actor_id | first_name | last_name | last_update |

+----------+------------+--------------+---------------------+

| 1 | PENELOPE | GUINESS | 2006-02-15 04:34:33 |

| 2 | NICK | WAHLBERG | 2006-02-15 04:34:33 |

| 3 | ED | CHASE | 2006-02-15 04:34:33 |

| 4 | JENNIFER | DAVIS | 2006-02-15 04:34:33 |

| 5 | JOHNNY | LOLLOBRIGIDA | 2006-02-15 04:34:33 |

| 6 | BETTE | NICHOLSON | 2006-02-15 04:34:33 |

| 7 | GRACE | MOSTEL | 2006-02-15 04:34:33 |

| 8 | MATTHEW | JOHANSSON | 2006-02-15 04:34:33 |

| 9 | JOE | SWANK | 2006-02-15 04:34:33 |

| 10 | CHRISTIAN | GABLE | 2006-02-15 04:34:33 |

+----------+------------+--------------+---------------------+

10 rows in set (0.00sec)

root@localhost : sakila 23:19:37> CHECKSUM TABLEactor;+--------------+------------+

| Table | Checksum |

+--------------+------------+

| sakila.actor | 2472295518 |

+--------------+------------+

1 row in set (0.00sec)

root@localhost : sakila 23:19:40>

可以发现和drop table之前完全一致。到这里数据就恢复完成啦。希望小伙伴们永远不要使用到改工具。

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值