一、下載安裝
1.1 下載地址
git clone https://github.com/twindb/undrop-for-innodb.git
1.2 安裝
安裝依賴包
yum install -y make gcc flex bison
編譯安裝
cd undrop-for-innodb make
增加用於恢復表結構的工具sys_parse
gcc `$basedir/bin/mysql_config --cflags` `$basedir/bin/mysql_config --libs` -o sys_parser sys_parser.c
$basedir 是 MySQL的安裝路徑
二、開始測試
2.1 測試數據
undrop-for-innodb中帶了一個 sakila 庫,不過我在測試的時候,用的是官方的,address 表有一個字段類型是 geometry。恢復的時候報錯,有興趣的朋友可以試一下
下載地址 https://dev.mysql.com/doc/index-other.html
2.2 刪除表
set foreign_key_checks = 0; checksum table customer; +-----------------+-----------+ | Table | Checksum | +-----------------+-----------+ | sakila.customer | 399782750 | +-----------------+-----------+ drop table customer;
checksum table 用來做恢復后的校驗
2.3 數據恢復
2.3.1 表結構恢復
使用工具 stream_parser
解析文件內容。
./stream_parser -f /data/mysql/mysql_3306/data/ibdata1
執行完畢后會在當前目錄下生成文件夾 pages-ibdata1
, 目錄下按照每個頁為一個文件,分為索引頁和數據較大的 BLOB 頁。系統表的話,是存在索引頁中的。使用另外一個重要的工具 c_parser
來解析頁的內容。
./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000001.page -t dictionary/SYS_TABLES.sql > dumps/default/SYS_TABLES 2> dumps/default/SYS_TABLES.sql ./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000003.page -t dictionary/SYS_INDEXES.sql > dumps/default/SYS_INDEXES 2> dumps/default/SYS_INDEXES.sql ./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000002.page -t dictionary/SYS_COLUMNS.sql > dumps/default/SYS_COLUMNS 2> dumps/default/SYS_COLUMNS.sql ./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000004.page -t dictionary/SYS_FIELDS.sql > dumps/default/SYS_FIELDS 2> dumps/default/SYS_FIELDS.sql
參數解析:
- 4 表示文件格式是 REDUNDANT,系統表的格式默認值。另外可以取值 5 表示 COMPACT 格式,6 表示 MySQL 5.6 格式。
- D 表示只恢復被刪除的記錄。
- f 后面跟着文件。
- t 后面跟着 CREATE TABLE 語句,需要根據表的格式來解析文件。
得到的結果 ‘SYS_TABLES’ 字段后面的就是系統表 SYS_TABLE 中對應存的記錄。
創建恢復數據庫 recover,用來存放恢復的系統表
create database recover
導入系統表
mysql recover < dictionary/SYS_TABLES.sql mysql recover < dictionary/SYS_INDEXES.sql mysql recover < dictionary/SYS_FIELDS.sql mysql recover < dictionary/SYS_COLUMNS.sql mysql recover < dumps/default/SYS_TABLES.sql mysql recover < dumps/default/SYS_INDEXES.sql mysql recover < dumps/default/SYS_FIELDS.sql mysql recover < dumps/default/SYS_COLUMNS.sql
dictionary目錄下是建表SQL,dumps/default/是剛才解析 page 得到的sql
解析表結構
./sys_parser -h'127.0.0.1' -uroot -p'abc_1234' -d recover sakila/customer CREATE TABLE `customer`( `customer_id` SMALLINT UNSIGNED NOT NULL, `store_id` TINYINT UNSIGNED NOT NULL, `first_name` VARCHAR(45) CHARACTER SET 'utf8' COLLATE 'utf8_general_ci' NOT NULL, `last_name` VARCHAR(45) CHARACTER SET 'utf8' COLLATE 'utf8_general_ci' NOT NULL, `email` VARCHAR(50) CHARACTER SET 'utf8' COLLATE 'utf8_general_ci', `address_id` SMALLINT UNSIGNED NOT NULL, `active` TINYINT NOT NULL, `create_date` DATETIME NOT NULL, `last_update` TIMESTAMP, PRIMARY KEY (`customer_id`) ) ENGINE=InnoDB;
對比發現,恢復出來的 CREATE TABLE 語句相比原來創建的語句信息量有點缺少,因為 innodb 系統表里面存的數據相比 frm 文件是不足的,比如 AUTO_INCREMENT, DECIMAL 類型的精度信息都會缺失,也不會恢復二級索引,外建等。
2.3.2 表數據恢復
找到表 customer 的 table_id
grep customer dumps/default/SYS_TABLES 00000018501F 3C00000188063B SYS_TABLES "sakila/customer" 480 9 33 0 80 "" 475 00000018501F 3C00000188063B SYS_TABLES "sakila/customer" 480 9 33 0 80 "" 475
再根據 table_id 找到 index_id
grep 480 dumps/default/SYS_INDEXES 00000018501F 3C0000018802CB SYS_INDEXES 480 848 "PRIMARY" 1 3 475 4294967295 00000018501F 3C00000188033D SYS_INDEXES 480 849 "idx\_fk\_store\_id" 1 0 475 4294967295 00000018501F 3C0000018803AF SYS_INDEXES 480 850 "idx\_fk\_address\_id" 1 0 475 4294967295 00000018501F 3C000001880421 SYS_INDEXES 480 851 "idx\_last\_name" 1 0 475 4294967295 00000018501F 3C0000018802CB SYS_INDEXES 480 848 "PRIMARY" 1 3 475 4294967295 00000018501F 3C00000188033D SYS_INDEXES 480 849 "idx\_fk\_store\_id" 1 0 475 4294967295 00000018501F 3C0000018803AF SYS_INDEXES 480 850 "idx\_fk\_address\_id" 1 0 475 4294967295 00000018501F 3C000001880421 SYS_INDEXES 480 851 "idx\_last\_name" 1 0 475 4294967295
grep 480 是對應 SYS_TABLE 的 TALE ID,848對應的 INDEX_ID
MySQL5.6之后,默認 innodb_file_per_table = on 這種情況下每個表是保存在各自的 ibd 文件中的,當 drop table 之后 ,ibd 文件會被刪除,此時最好能夠設置磁盤整體只讀,避免有其它進程重寫文件塊。stream_parser
這個工具不但可以讀文件,還可以讀磁盤,會根據 innodb 數據格式把數據頁讀出來。
找到被刪除的 ibd 文件的掛載磁盤
df -h Filesystem Size Used Avail Use% Mounted on /dev/mapper/centos-root 46G 13G 33G 28% / devtmpfs 1.9G 0 1.9G 0% /dev tmpfs 1.9G 0 1.9G 0% /dev/shm tmpfs 1.9G 89M 1.8G 5% /run tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup /dev/vda1 1014M 142M 873M 14% /boot tmpfs 379M 0 379M 0% /run/user/0
我的實驗環境是 /dev/mapper/centos-root
./stream_parser -f /dev/mapper/centos-root -t 50G
磁盤大小執行 stream_parser
,-t 表示磁盤的大小。執行的時候需要注意磁盤空間。跑完之后,在 undrop-for-innodb目錄下會有一個 pages-centos-root 目錄,其他環境不知道叫什么。類似 pages-ibdata1目錄,下面依然是 FIL_PAGE_INDEX 跟 FIL_PAGE_TYPE_BLOB。我們要找的頁在 FIL_PAGE_INDEX 目錄下。
pwd /root/undrop-for-innodb/pages-centos-root/FIL_PAGE_INDEX ll 0000000000000848.page -rw-r--r--. 1 root root 81920 Oct 22 01:25 0000000000000848.page
接下來解析 0000000000000848.page
./c_parser -6f pages-centos-root/FIL_PAGE_INDEX/0000000000000848.page -t customer.sql > dumps/default/customer 2> dumps/default/customer.sql
參數解析:
- 6 表示 MySQL 5.6 格式,4 表示文件格式是 REDUNDANT,系統表的格式默認值。另外可以取值 5 表示 COMPACT 格式。
- D 表示只恢復被刪除的記錄。
- f 后面跟着文件。
- t 后面跟着 CREATE TABLE 語句,需要根據表的格式來解析文件。customer.sql 是我們解析表結構的文件
有興趣的朋友可以看一下 dumps/default/customer.sql,是 load data 語法SQL
2.3.3 導入恢復的數據
mysql sakila < customer.sql
mysql sakila < dumps/default/customer.sql
沒有報錯就是導入成功了,接下來就是校驗數據了
checksum table customer; +-----------------+-----------+ | Table | Checksum | +-----------------+-----------+ | sakila.customer | 399782750 | +-----------------+-----------+
跟刪除表前的校驗值是一樣的。恢復成功。