【MySQL】Innodb 恢復工具介紹

yuntui發表於2016-11-03
      在和資料打交道的過程中,總會遇到誤刪除資料的情況,在沒有備份和binlog的情況下,如何恢復呢?本文介紹一個工具Percona Data Recovery Tool for InnoDB 
使用 該工具的 注意事項:
1 The tools work only for InnoDB/XtraDB tables, and will not work with MyISAM tables. Percona does have a preliminary set of tools for MyISAM data recovery;  
2 The tools work on a saved copy of your data files, not on the running MySQL server.
3 There is no guarantee. Even with these tools, data is sometimes unrecoverable. For example, data that is overwritten cannot be recovered with these tools. There may be file system specific or physical means to recover overwritten data.
如果資料被覆蓋,則使用該工具無法恢復。必須使用系統或者物理方法來恢復
4 Time is of the essence. The best chance for recovery comes when you act immediately to save a copy of your raw data files as soon as you discover the loss or corruption.
  當發生誤刪除資料時必須立即備份表檔案以便確保資料被覆蓋或者損壞。
5 There is manual work to do. Not everything is automatic.
6 Recovery depends on knowing your data. As part of the process you may have to choose between two versions of your data. The better you know your data, the better the chance you'll be able to recover it.
需要理解的是innodb-tools工具不是透過連線到線上的database進行資料恢復,而是透過離線複製資料的方式進行的。
注意:不要在MySQL執行的時候,直接複製InnoDB檔案,這樣是不安全的,會影響資料恢復過程。不過這點我做了測試,在資料庫執行的時候是可以進行資料庫恢復的。
    
一 安裝
 進入解壓後根目錄下的mysql-source目錄,執行配置命令,不執行make命令
 wget
 cd percona-data-recovery-tool-for-innodb-0.5/mysql_source/
 ./configure
 cd ..
 make 
編譯生成page_parser和constraints_parser工具 
注意create_defs.pl指令碼需要依賴DBD,DBI,安裝過程中可能會遇到錯誤。

二 模擬誤刪除資料

root@127.0.0.1 : test 22:12:22> delete from mac where id < 51398;
Query OK, 4999 rows affected (0.62 sec)
root@127.0.0.1 : test 22:12:29> 

三  獲取資料頁
InnoDB頁的預設大小是16K,innodb的page分為兩大部分,一部分一級索引部分,另一部分為二級索引部分。page_parser工具透過讀取資料檔案,根據頁頭中的index ID,複製每個頁到一個單獨的檔案中。
如果你的my.cnf配置了innodb_file_per_table=1,那麼系統已經幫你實現上述過程。所有需要的頁都在單獨的.ibd檔案,而且通常你不需要再切分它
如果.ibd檔案中可能包含多個index,那麼將頁單獨切分開還是有必要的。如果MySQL server沒有配置innodb_file_per_table,那麼資料會被儲存在一個全域性的表名稱空間,這時候就需要按頁對檔案進行切分。 
[root@rac1 recovery-tool]# ./page_parser  -5 -f /opt/mysql/data/test/mac.ibd   
-5:代表 row format為Compact
-f:代表要解析的檔案
輸出資訊:       
Opening file: /opt/mysql/data/test/mac.ibd:
2051            ID of device containing file
20283635                inode number
33200           protection
1               number of hard links
103             user ID of owner
106             group ID of owner
0               device ID (if special file)
11534336                total size, in bytes
4096            blocksize for filesystem I/O
22560           number of blocks allocated
1377958353      time of last access
1377958359      time of last modification
1377958359      time of last status change
11534336        Size to process in bytes
104857600       Disk cache size in bytes
[root@rac1 recovery-tool]# less pages-1377958391/FIL_PAGE_INDEX/0-205
0-2057/ 0-2058/ 0-2059/ 
以上三個為索引檔案 0-2057/主鍵,0-2058/ 0-2059/ 二級索引。可以安裝開啟innodb_table_monitor獲取。
四 獲取表結構的定義
 
 ./create_defs.pl  --host 127.0.0.1 --user root --port 3306 --db test --table mac > include/table_defs.h  
[root@rac1 recovery-tool]# more include/table_defs.h
#ifndef table_defs_h
#define table_defs_h

// Table definitions
table_def_t table_definitions[] = {
        {
                name: "mac",
                {
                        { /* 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: "mac",
                                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
                        },
                        { /* varchar(50) */
                                name: "name",
                                type: FT_CHAR,
                                min_length: 0,
                                max_length: 150,

                                has_limits: FALSE,
                                limits: {
                                        can_be_null: TRUE,
                                        char_min_len: 0,
                                        char_max_len: 150,
                                        char_ascii_only: TRUE
                                },

                                can_be_null: TRUE
                        },
                        { /* tinyint(4) */
                                name: "scope",
                                type: FT_INT,
                                fixed_length: 1,

                                has_limits: FALSE,
                                limits: {
                                        can_be_null: TRUE,
                                        int_min_val: -128,
                                        int_max_val: 127
                                },

                                can_be_null: TRUE
                        },
                        { /* datetime */
                                name: "gmt_create",
                                type: FT_DATETIME,
                                fixed_length: 8,

                                can_be_null: FALSE
                        },
                        { /* datetime */
                                name: "gmt_modify",
                                type: FT_DATETIME,
                                fixed_length: 8,

                                can_be_null: FALSE
                        },
                        { type: FT_NONE }
                }
        },
};

#endif
五 根據include/table_defs.h,重新編譯constraints_parser工具:
[root@rac1 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 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 
恢復誤刪除的資料:
./constraints_parser -D -5 -f pages-1377958391/FIL_PAGE_INDEX/0-2057/ > /tmp/mac.rec
LOAD DATA INFILE '/root/recovery-tool/dumps/default/mac' REPLACE INTO TABLE `mac` FIELDS TERMINATED BY '\t' OPTIONALLY ENCLOSED BY '"' LINES STARTING BY 'mac\t' (id, mac, name, scope, gmt_create, gmt_modify);

執行 constraints_parser 命令會生成上午load data 的命令,在資料庫中執行上述命令即可.
root@127.0.0.1 : test 22:20:54> select count(1) from mac;
+----------+
| count(1) |
+----------+
|     9973 |
+----------+
1 row in set (0.00 sec)
root@127.0.0.1 : test 22:21:09> LOAD DATA INFILE '/tmp/mac.rec' REPLACE INTO TABLE `mac` FIELDS TERMINATED BY '\t' OPTIONALLY ENCLOSED BY '"' LINES STARTING BY 'mac\t' (id, mac, name, scope, gmt_create, gmt_modify);
Query OK, 4999 rows affected (0.22 sec)
Records: 4999  Deleted: 0  Skipped: 0  Warnings: 0
root@127.0.0.1 : test 22:21:13> select count(1) from mac;
+----------+
| count(1) |
+----------+
|    14972 |
+----------+
1 row in set (0.00 sec)
root@127.0.0.1 : test 22:21:18> exit

總結
 1 整個恢復過程並不順利,Percona 依賴於perl,在安裝的時候遇到DBD安裝不了的問題。
 2 可以恢復delete的資料,如果執行truncate table 是恢復失敗的,drop的時候ibd檔案丟同樣沒有檔案來獲取資料頁而無法進行恢復。
  對於truncate的測試例子大家可以手動測試一下。
  
參考文章

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30633755/viewspace-2127644/,如需轉載,請註明出處,否則將追究法律責任。

相關文章