DROP TABLE 恢復【一】

當DROP TABLE指令敲下的時候,你很爽,你有考慮事後果麼?若是該表真的沒用,你DROP到無所謂,若是還有用的,這時你確定嚇慘了吧,若是你有備份,那麼恭喜你,逃過一劫,若是沒有備份呢?這時就該絕望了?NO! 若是你的表是innodb表,那麼還有但願挽救,若是是myisam表,那麼真的沒救了。前面文章介紹了 Recover InnoDB dictionary,這是恢復數據的前提。恢復innodb字典信息使用的是TwinDB recovery toolkit,咱們恢復數據也是使用該工具。下面的案例是基於innodb_file_per_table=OFF的前提下,即便用共享表空間,全部的信息都保存在ibdata1中。使用獨立表空間DROP TABLE後數據恢復將在後面的文章介紹。html

錯誤的操做--刪除表node

用到的示例數據庫仍是sakila,關於下載地址前面的文章有地址。將模擬把sakila庫中的actor表刪除後進行恢復。mysql

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恢復數據sql

如今actor表已經刪除,但表中的信息仍然存與ibdata1中。該數據保持不變,直到InnoDB的重用空閒的頁。咱們須要儘快中止mysqld進程。
對於恢復,咱們將使用TwinDB恢復工具包。看看我前面的文章Recover InnoDB dictionary數據庫

解析innodb表空間(ibdata1)工具

InnoDB將全部數據存儲在B +樹索引。 一個表有隻有一個彙集索引,全部字段存儲在這裏。 若是表有輔助索引,由index_id標識每一個索引。spa

若是咱們要恢復一個表,咱們必須找到屬於特定index_id的全部頁面。code

stream_parser讀取InnoDB表和排序按類型和每一個index_id的InnoDB的頁面。orm

[root@mysql-server-01 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:         2055
inode number:                     77760163
protection:                         100660 (regular file)
number of hard links:                    1
user ID of owner:                      498
group ID of owner:                     500
device ID (if special file):             0
blocksize for filesystem I/O:         4096
number 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 2014
total size, in bytes:             27262976 (26.000 MiB)

Size to process:                  27262976 (26.000 MiB)
All workers finished in 0 sec
[root@mysql-server-01 undrop-for-innodb]# 

使用stream_parser將把數據從page保存到pages-ibdata1server

[root@mysql-server-01 FIL_PAGE_INDEX]# pwd
/root/undrop-for-innodb/pages-ibdata1/FIL_PAGE_INDEX
[root@mysql-server-01 FIL_PAGE_INDEX]# ll 
total 6976
-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/actor
000000000344    45000002B902C8  SYS_TABLES      "sakila/actor"  13      4       1       0       0       ""      0
000000000344    45000002B902C8  SYS_TABLES      "sakila/actor"  13      4       1       0       0       ""      0
SET 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       4294967295
SET 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  actor   1       "PENELOPE"      "GUINESS"       "2006-02-15 04:34:33"
00000000032C    AD00000175011A  actor   2       "NICK"  "WAHLBERG"      "2006-02-15 04:34:33"
00000000032C    AD000001750124  actor   3       "ED"    "CHASE" "2006-02-15 04:34:33"
00000000032C    AD00000175012E  actor   4       "JENNIFER"      "DAVIS" "2006-02-15 04:34:33"
00000000032C    AD000001750138  actor   5       "JOHNNY"        "LOLLOBRIGIDA"  "2006-02-15 04:34:33"
00000000032C    AD000001750142  actor   6       "BETTE" "NICHOLSON"     "2006-02-15 04:34:33"
00000000032C    AD00000175014C  actor   7       "GRACE" "MOSTEL"        "2006-02-15 04:34:33"
00000000032C    AD000001750156  actor   8       "MATTHEW"       "JOHANSSON"     "2006-02-15 04:34:33"
00000000032C    AD000001750160  actor   9       "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 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/

相關文章
相關標籤/搜索