当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.01 sec) root@localhost : sakila 21:34:25>
root@localhost : sakila 21:34:25> CHECKSUM TABLE actor;
+--------------+------------+
| Table | Checksum |
+--------------+------------+
| sakila.actor | 2472295518 |
+--------------+------------+
1 row in set (0.07 sec) root@localhost : sakila 21:35:30> SET foreign_key_checks=OFF;
Query OK, 0 rows affected (0.00 sec) root@localhost : sakila 21:35:46> DROP TABLE actor;
Query OK, 0 rows affected (0.07 sec) 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- undrop-for-innodb]# ./stream_parser -f /data/mysql/user_3306/data/ibdata1
Opening file: /data/mysql/user_3306/data/ibdata1
File information: ID of device containing file:
inode number:
protection: (regular file)
number of hard links:
user ID of owner:
group ID of owner:
device ID (if special file):
blocksize for filesystem I/O:
number of blocks allocated:
time of last access: Sun Aug ::
time of last modification: Sun Aug ::
time of last status change: Sun Aug ::
total size, in bytes: (26.000 MiB) Size to process: (26.000 MiB)
All workers finished in sec
[root@mysql-server- undrop-for-innodb]#

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

[root@mysql-server- FIL_PAGE_INDEX]# pwd
/root/undrop-for-innodb/pages-ibdata1/FIL_PAGE_INDEX
[root@mysql-server- FIL_PAGE_INDEX]# ll
total
-rw-r--r-- root root Aug : .page
-rw-r--r-- root root Aug : .page
-rw-r--r-- root root Aug : .page
-rw-r--r-- root root Aug : .page
-rw-r--r-- root root Aug : .page
。。。。。。。。。。。。。。。。。。。。。。。。。
-rw-r--r-- root root Aug : .page
-rw-r--r-- root root Aug : .page
-rw-r--r-- root root Aug : .page
-rw-r--r-- root root Aug : .page
-rw-r--r-- root root Aug : .page
-rw-r--r-- root root Aug : .page
-rw-r--r-- root root Aug : .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- undrop-for-innodb]# ./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/.page -t dictionary/SYS_TABLES.sql | grep sakila/actor
45000002B902C8 SYS_TABLES "sakila/actor" ""
45000002B902C8 SYS_TABLES "sakila/actor" ""
SET FOREIGN_KEY_CHECKS=;
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- undrop-for-innodb]#

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

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

[root@mysql-server- undrop-for-innodb]# ./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/.page -t dictionary/SYS_INDEXES.sql | grep
45000002B90145 SYS_INDEXES "PRIMARY"
45000002B901B7 SYS_INDEXES "idx\_actor\_last\_name"
45000002B90145 SYS_INDEXES "PRIMARY"
45000002B901B7 SYS_INDEXES "idx\_actor\_last\_name"
45000002B90145 SYS_INDEXES "PRIMARY"
45000002B901B7 SYS_INDEXES "idx\_actor\_last\_name"
SET FOREIGN_KEY_CHECKS=;
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- undrop-for-innodb]#

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

[root@mysql-server- undrop-for-innodb]# ./c_parser -6f pages-ibdata1/FIL_PAGE_INDEX/.page -t sakila/actor.sql |  head -
-- Page id: , Format: COMPACT, Records list: Valid, Expected records: ( )
00000000032C AD000001750110 actor "PENELOPE" "GUINESS" "2006-02-15 04:34:33"
00000000032C AD00000175011A actor "NICK" "WAHLBERG" "2006-02-15 04:34:33"
00000000032C AD000001750124 actor "ED" "CHASE" "2006-02-15 04:34:33"
00000000032C AD00000175012E actor "JENNIFER" "DAVIS" "2006-02-15 04:34:33"
00000000032C AD000001750138 actor "JOHNNY" "LOLLOBRIGIDA" "2006-02-15 04:34:33"
00000000032C AD000001750142 actor "BETTE" "NICHOLSON" "2006-02-15 04:34:33"
00000000032C AD00000175014C actor "GRACE" "MOSTEL" "2006-02-15 04:34:33"
00000000032C AD000001750156 actor "MATTHEW" "JOHANSSON" "2006-02-15 04:34:33"
00000000032C AD000001750160 actor "JOE" "SWANK" "2006-02-15 04:34:33"
[root@mysql-server- undrop-for-innodb]#

看见上面的输出,是不是觉得希望来了?哈哈
上面的结果正是我们想要的,我们现在把数据存贮在文件中,然后倒入,创建dump/default目录存储数据。

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

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

[root@mysql-server- undrop-for-innodb]# cat dumps/default/actor_load.sql
SET FOREIGN_KEY_CHECKS=;
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- 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 table actor\G
*************************** 1. row ***************************
Table: actor
Create Table: CREATE TABLE `actor` (
`actor_id` smallint(5) unsigned NOT NULL AUTO_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=utf8
1 row in set (0.00 sec) 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 id is 18
Server version: 5.5.37-log MySQL Community Server (GPL) Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. root@localhost : (none) 23:17:16> use sakila
Database changed
root@localhost : sakila 23:17:19> source dumps/default/actor_load.sql;
Query OK, 0 rows affected (0.00 sec) Query OK, 600 rows affected (0.08 sec)
Records: 400 Deleted: 200 Skipped: 0 Warnings: 0 root@localhost : sakila 23:17:22>

检查恢复的数据

root@localhost : sakila 23:19:00> SELECT COUNT(*) FROM actor;
+----------+
| COUNT(*) |
+----------+
| 200 |
+----------+
1 row in set (0.00 sec) 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.00 sec) root@localhost : sakila 23:19:37> CHECKSUM TABLE actor;
+--------------+------------+
| Table | Checksum |
+--------------+------------+
| sakila.actor | 2472295518 |
+--------------+------------+
1 row in set (0.00 sec) root@localhost : sakila 23:19:40>

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

参考资料

https://twindb.com/recover-innodb-table-after-drop-table-innodb/

DROP TABLE 恢复【一】的更多相关文章

  1. Hadoop之hive的drop table恢复

    一.引言: 快下班的时候我开发同事问能不能将hive中drop掉的数据恢复过来,我记得是有开回收站的,当时我回答说可以恢复的. 二.恢复过程: 在之前我有对hadoop的回收站有过了解,就是将hdfs ...

  2. MySQL5.7下面,误操作导致的drop table db1.tb1; 的恢复方法:

    MySQL5.7下面,误操作导致的drop table db1.tb1; 的恢复方法: 0.停业务数据写入.[iptables封禁] 1.从备份服务器上拉取最新的一个全备文件,恢复到一个临时的服务器上 ...

  3. hive drop和恢复partition external table

    在hdfs目录:/user/xx/table/test_external 保存 test_external 表数据 先建表,使用列式存储格式 CREATE external TABLE `test_e ...

  4. DROP TABLE ** CASCADE CONSTRAINTS PURGE删除表的时候级联删除从表外键

    1.关于 cascade constraints 假设A为主表(既含有某一主键的表),B为从表(即引用了A的主键作为外键). 则当删除A表时,如不特殊说明,则 drop table A 系统会出现错误 ...

  5. Oracle10g 回收站及彻底删除table : drop table xx purge

    drop后的表被放在回收站(user_recyclebin)里,而不是直接删除掉.这样,回收站里的表信息就可以被恢复,或彻底清除. 1.通过查询回收站user_recyclebin获取被删除的表信息, ...

  6. oracle drop table and purge

    一.drop表 执行drop table xx 语句 drop后的表被放在回收站(user_recyclebin)里,而不是直接删除掉.这样,回收站里的表信息就可以被恢复,或彻底清除. 通过查询回收站 ...

  7. Oracle Drop表并未直接删除 drop table xx purge

    drop表 执行drop table xx 语句     drop后的表被放在回收站(user_recyclebin)里,而不是直接删除掉.这样,回收站里的表信息就可以被恢复,或彻底清除.     通 ...

  8. Drop Table对MySQL的性能影响分析

    [问题描述] 最近碰到有台MySQL实例出现了MySQL服务短暂hang死,表现为瞬间的并发线程上升,连接数暴增. 排查Error Log文件中有page_cleaner超时的信息,引起我们的关注: ...

  9. Oracle Drop Table

    DROP TABLE 使用DROP TABLE语句将表或对象表移动到回收站或从数据库中完全删除表及其所有数据. 注:除非指定purge子句,否则drop table语句不会将表占用的空间释放回表空间供 ...

随机推荐

  1. _ZNote_Qt_对话框_模态非模态

    QDialog(及其子类,以及所有Qt::Dialog类型的类)的对于其 parent 指针都有额外的解释:如果 parent 为 NULL,则该对话框会作为一个顶层窗口,否则则作为其父组件的子对话框 ...

  2. ubuntu 开机自启(2B的经历)

    上午写了很细致的开机自启说明文档(需打开terminal进行输出认证).睡了一下午,回来楼主说,联想PM要用Ubuntu Server 当服务器,必须用命令行实现.. 连续各种百度谷歌,看了N多文档, ...

  3. JDBC架构

    JDBC API支持两层和三层处理模型进行数据库访问,但在一般的JDBC体系结构由两层组成: JDBC API: 提供了应用程序对JDBC的管理连接. JDBC Driver API: 支持JDBC管 ...

  4. 6.form表单四种提交方式

    一.使用jquery的ajax方式提交: 二.使用easyui的form组件内置的submit方法提交: 三.先定义表单,然后使用submit方法提交: 四.先定义表单,然后按下enter键时提交:

  5. hdu 5056 所有字母数都<=k的子串数目

    <a target=_blank href="http://acm.hdu.edu.cn/showproblem.php?pid=5056" style="font ...

  6. [ 9.13 ]CF每日一题系列—— 340A GCD & LCM

    Description: [ 着实比较羞愧,都想着去暴力,把算法(方法)也忘了] A只涂x,2x,3x……,B只涂y,2y,3y……问你A和B共同涂的墙的个数 Solution: 就是求x和y的lcm ...

  7. GetFileOpenName()、GetFilesavename

    GetFileOpenName() 功能显示打开文件对话框,让用户选择要打开的文件. 语法:GetFileOpenName(title,pathname,filename{,extension{,fi ...

  8. ArrayBlockingQueue源码解析(1)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 注意:在阅读本文之前或在阅读的过程中,需要用到ReentrantLock,内容见<第五章 Reentr ...

  9. 二进制转化为十进制Java实现

    二进制转化为十进制 ①按权展开方法Java实现 /* * 按权展开法 */ public static double BinToTen1(String binary) { //查找该二进制是否存在小数 ...

  10. 记一次线上事故的JVM内存学习

    今天线上的hadoop集群崩溃了,现象是namenode一直在GC,长时间无法正常服务.最后运维大神各种倒腾内存,GC稳定后,服务正常.虽说全程在打酱油,但是也跟着学习不少的东西. 第一个问题:为什么 ...