MySQL數據庫InnoDB數據恢停工具的應用小結詳解。本站提示廣大學習愛好者:(MySQL數據庫InnoDB數據恢停工具的應用小結詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是MySQL數據庫InnoDB數據恢停工具的應用小結詳解正文
本文從現實應用經歷動身,引見一款開源的MySQL數據庫InnoDB數據恢停工具:innodb-tools,它經由過程從原始數據文件中提取表的行記載,完成從喪失的或許被損壞的MySQL表中恢單數據。例如,當你不當心履行DROP TABLE、TRUNCATE TABLE或許DROP DATABASE以後,可以經由過程以下方法恢單數據。
以下內容年夜部門參考自:Percona Data Recovery Tool for InnoDB,文檔是英文的,並且寫的比擬艱澀,這裡是小我的實戰經歷總結,供年夜家參考進修。
在引見innodb-tools對象停止數據恢復之前,起首明白以下幾點:
1、這個對象只能對InnoDB/XtraDB表有用,而沒法恢復MyISAM表(注: Percona號稱有一套用於恢復MyISAM表的對象,然則自己未做測驗考試)。
2、這個對象是以保留的MySQL數據文件停止恢復的,而不消MySQL Server運轉。
3、不克不及包管數據總必定可被恢復。例如,被重寫的數據不克不及被恢復,這類情形下能夠須要針對體系或物理的方法來恢復,不屬於本對象的領域。
4、恢復的最好機會是當你發明數據喪失時,盡快備份MySQL數據文件。
5、應用這個對象須要手動做一些任務,其實不是全主動完成的。
6、恢復進程依附於你對喪失數據的懂得水平,在恢復進程中能夠須要在分歧版本的數據之間做出選擇。那末假如你越懂得本身的數據,恢復的能夠性就越年夜。
接上去,上面經由過程一個例子來引見若何經由過程這個對象停止恢復。
1. 條件前提
起首,須要懂得的是innodb-tools對象不是經由過程銜接到在線的database停止數據恢復,而是經由過程離線拷貝數據的方法停止的。留意:不要在MySQL運轉的時刻,直接拷貝InnoDB文件,如許是不平安的,會影響數據恢復進程。
為了完成數據恢復,必需曉得將要被恢復的表構造(列名、數據類型)。最簡略的方法就是SHOW CREATE TABLE,固然後續會引見幾種可替換的方法。是以,假如有一個MySQL server作為備份,即便數據是很早的乃至表中沒有記載,可以有助於應用innodb-tools對象停止恢復。不外這個不是必需的。
2. 簡略例子
mysql> TRUNCATE TABLE customer;
3. 構建對象
為了構建innodb-tools對象,須要依附於C編譯器、make對象等。
1、下載解壓innodb-tools對象源碼:
wget https://launchpad.net/percona-data-recovery-tool-for-innodb/trunk/release-0.5/+download/percona-data-recovery-tool-for-innodb-0.5.tar.gztar -zxvf percona-data-recovery-tool-for-innodb-0.5.tar.gz
2、進入解壓後根目次下的mysql-source目次,運轉設置裝備擺設敕令(注:不運轉make敕令):
cd percona-data-recovery-tool-for-innodb-0.5/mysql-source
./configure
3、完成設置裝備擺設步調後,回到解壓後的根目次,運轉make敕令,編譯生成page_parser和constraints_parser對象:
cd ..
make
page_parser對象將依據InnoDB的底層完成道理,解析表的頁和行構造。constraints_parser對象臨時不應用,後續還須要在界說表構造以後,從新編譯生成它。
假如編譯進程中湧現成績,點擊這裡。本文應用進程中沒有湧現成績,故不再逐個羅列。
4. 提取須要的頁
InnoDB頁的默許年夜小是16K,每一個頁屬於一個特定表中的一個特定的index。page_parser對象經由過程讀取數據文件,依據頁頭中的index ID,拷貝每一個頁到一個零丁的文件中。
假如你的MySQL server被設置裝備擺設為innodb_file_per_table=1,那末體系曾經幫你完成上述進程。一切須要的頁都在.ibd文件,並且平日你不須要再切分它。但是,假如.ibd文件中能夠包括多個index,那末將頁零丁切離開照樣有需要的。假如MySQL server沒有設置裝備擺設innodb_file_per_table,那末數據會被保留在一個全局的表定名空間(平日是一個名為ibdata1的文件,本文屬於這類情形),這時候候就須要按頁對文件停止切分。
4.1 切分頁
運轉page_parser對象停止切分:
•假如MySQL是5.0之前的版本,InnoDB采用的是REDUNDANT格局,運轉以下敕令:
./page_parser -4 -f /path/to/ibdata1
•假如MySQL是5.0版本,InnoDB采用的是COMPACT格局,運轉以下敕令:
./page_parser -5 -f /path/to/ibdata1
運轉後,page_parser對象會創立一個pages-<TIMESTAMP>的目次,個中TIMESTAMP是UNIX體系時光戳。在這個目次下,為每一個index ID,以頁的index ID創立一個子目次。例如:
pages-1330842944/FIL_PAGE_INDEX/0-1/1-00000008.page
pages-1330842944/FIL_PAGE_INDEX/0-1/6-00000008.page
4.2 選擇須要的Index ID
普通來講,我們須要依據表的主鍵(PRIMARY index)停止恢復,主鍵中包括了一切的行。以下是一些可以完成的步調:
假如數據庫仍處於運轉狀況,而且表沒有被drop失落,那末可以啟動InnoDB Tablespace Monitor,輸入一切表和indexes,index IDs到MySQL server的毛病日記文件。創立innodb_table_monitor表用於搜集innodb存儲引擎表及其索引的存儲方法:
mysql> CREATE TABLE innodb_table_monitor (id int) ENGINE=InnoDB;
假如innodb_table_monitor曾經存在,drop表然後從新create表。等MySQL毛病日記輸入後,可以drop失落這張表以停滯打印輸入更多的監控。一個輸入的例子以下:
TABLE: name sakila/customer, id 0 142, columns 13, indexes 4, appr.rows 0
COLUMNS: customer_id: DATA_INT len 2 prec 0; store_id: DATA_INT len 1 prec 0; first_name: type 12 len 135 prec 0; last_name: type 12 len 135 prec 0; email:
type 12 len 150 prec 0; address_id: DATA_INT len 2 prec 0; active: DATA_INT len 1 prec 0; create_date: DATA_INT len 8 prec 0; last_update: DATA_INT len 4 pr
ec 0; DB_ROW_ID: DATA_SYS prtype 256 len 6 prec 0; DB_TRX_ID: DATA_SYS prtype 257 len 6 prec 0; DB_ROLL_PTR: DATA_SYS prtype 258 len 7 prec 0;
INDEX: name PRIMARY, id 0 286, fields 1/11, type 3
root page 50, appr.key vals 0, leaf pages 1, size pages 1
FIELDS: customer_id DB_TRX_ID DB_ROLL_PTR store_id first_name last_name email address_id active create_date last_update
INDEX: name idx_fk_store_id, id 0 287, fields 1/2, type 0
root page 56, appr.key vals 0, leaf pages 1, size pages 1
FIELDS: store_id customer_id
INDEX: name idx_fk_address_id, id 0 288, fields 1/2, type 0
root page 63, appr.key vals 0, leaf pages 1, size pages 1
FIELDS: address_id customer_id
INDEX: name idx_last_name, id 0 289, fields 1/2, type 0
root page 1493, appr.key vals 0, leaf pages 1, size pages 1
FIELDS: last_name customer_id
這裡,我們恢復的是sakila庫下的customer表,從下面可以獲得其主鍵信息:
INDEX: name PRIMARY, id 0 286, fields 1/11, type 3
Index ID是0 256,是以我們須要恢復的InnoDB頁位於0-256子目次下。
備注:參考文檔原文中之描寫了以上這類獲得表的index ID的辦法,本文在現實操作中,采用了更簡略的一種方法,即直接恢復page_parser生成的一切InnoDB頁。理論證實這類辦法也是可行的:)
5. 生成表界說
步調4中,我們曾經找到了須要的數據,接上去須要找到表構造,創立表界說,將其編譯到constraints_parser中,然後應用這個對象從InnoDB頁中提取表中的行。
表界說包括了表中的列、列次序、數據類型。假如MySQL server仍處於運轉且表未被drop失落,那末簡略適用SHOW CREATE TABLE便可以搜集到這些信息。接上去將應用這些表構造信息來創立一個C構造體標識的表界說,然後編譯到constraints_parser對象。C構造體的界說寄存在include/table_defs.h中。
最簡略的方法是create_defs.pl Perl 劇本,銜接到MySQL server,讀取SHOW CREATE TABLE的成果,輸入生成的表界說到尺度輸入。上面是個例子,個中直接將成果重定向到了include/table_defs.h中:
If possible, the easiest way to create the table definition is with the create_defs.pl Perl script. It connects to the MySQL server and reads SHOW CREATE TABLE output, and prints the generated definition to its standard output. Here is an example:
$ ./create_defs.pl --host=localhost --user=root --password=123456 --db=sakila --table=customer > include/table_defs.h
上面是例子中的表構造:
CREATE TABLE `customer` (
`customer_id` smallint(5) UNSIGNED NOT NULL AUTO_INCREMENT,
`store_id` tinyint(3) UNSIGNED NOT NULL,
`first_name` varchar(45) NOT NULL,
`last_name` varchar(45) NOT NULL,
`email` varchar(50) DEFAULT NULL,
`address_id` smallint(5) UNSIGNED NOT NULL,
`active` tinyint(1) NOT NULL DEFAULT '1',
`create_date` datetime NOT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`customer_id`),
KEY `idx_fk_store_id` (`store_id`),
KEY `idx_fk_address_id` (`address_id`),
KEY `idx_last_name` (`last_name`),
CONSTRAINT `fk_customer_address` FOREIGN KEY (`address_id`) REFERENCES `address` (`address_id`) ON UPDATE CASCADE,
CONSTRAINT `fk_customer_store` FOREIGN KEY (`store_id`) REFERENCES `store` (`store_id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8
上面是生成的表界說:
#ifndef table_defs_h
#define table_defs_h
// Table definitions
table_def_t table_definitions[] = {
{
name: "customer",
{
{ /* smallint(5) unsigned */
name: "customer_id",
type: FT_UINT,
fixed_length: 2,
has_limits: TRUE,
limits: {
can_be_null: FALSE,
uint_min_val: 0,
uint_max_val: 65535
},
can_be_null: FALSE
},
{ /* Innodb's internally used field */
name: "DB_TRX_ID",
type: FT_INTERNAL,
fixed_length: 6,
can_be_null: FALSE
},
{ /* Innodb's internally used field */
name: "DB_ROLL_PTR",
type: FT_INTERNAL,
fixed_length: 7,
can_be_null: FALSE
},
{ /* tinyint(3) unsigned */
name: "store_id",
type: FT_UINT,
fixed_length: 1,
has_limits: TRUE,
limits: {
can_be_null: FALSE,
uint_min_val: 0,
uint_max_val: 255
},
can_be_null: FALSE
},
{ /* varchar(45) */
name: "first_name",
type: FT_CHAR,
min_length: 0,
max_length: 45,
has_limits: TRUE,
limits: {
can_be_null: FALSE,
char_min_len: 0,
char_max_len: 45,
char_ascii_only: TRUE
},
can_be_null: FALSE
},
{ /* varchar(45) */
name: "last_name",
type: FT_CHAR,
min_length: 0,
max_length: 45,
has_limits: TRUE,
limits: {
can_be_null: FALSE,
char_min_len: 0,
char_max_len: 45,
char_ascii_only: TRUE
},
can_be_null: FALSE
},
{ /* varchar(50) */
name: "email",
type: FT_CHAR,
min_length: 0,
max_length: 50,
has_limits: TRUE,
limits: {
can_be_null: TRUE,
char_min_len: 0,
char_max_len: 50,
char_ascii_only: TRUE
},
can_be_null: TRUE
},
{ /* smallint(5) unsigned */
name: "address_id",
type: FT_UINT,
fixed_length: 2,
has_limits: TRUE,
limits: {
can_be_null: FALSE,
uint_min_val: 0,
uint_max_val: 65535
},
can_be_null: FALSE
},
{ /* tinyint(1) */
name: "active",
type: FT_INT,
fixed_length: 1,
can_be_null: FALSE
},
{ /* datetime */
name: "create_date",
type: FT_DATETIME,
fixed_length: 8,
can_be_null: FALSE
},
{ /* timestamp */
name: "last_update",
type: FT_UINT,
fixed_length: 4,
can_be_null: FALSE
},
{ type: FT_NONE }
}
},
};
#endif
假如須要,可以依據須要編纂修正include/table_defs.h;然後依據include/table_defs.h,從新編譯constraints_parser對象:
$ make
gcc -DHAVE_OFFSET64_T -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE=1 -D_LARGEFILE_SOURCE=1 -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 -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 -g -I include -I mysql-source/include -I mysql-source/innobase/include -o page_parser page_parser.c lib/tables_dict.o lib/libut.a
6. 從頁中提取行記載
6.1 歸並頁到一個文件
後面曾經提到,我們須要恢復的index ID 0 286,包括數據的頁位於pages-1246363747/0-286/ 目次。
total 120
-rw-r--r-- 1 root root 16384 Jun 30 05:09 1254-00001254.page
-rw-r--r-- 1 root root 16384 Jun 30 05:09 1255-00001255.page
-rw-r--r-- 1 root root 16384 Jun 30 05:09 1256-00001256.page
-rw-r--r-- 1 root root 16384 Jun 30 05:09 1257-00001257.page
-rw-r--r-- 1 root root 16384 Jun 30 05:09 50-00000050.page
-rw-r--r-- 1 root root 16384 Jun 30 05:09 74-00000050.page
輸出以下敕令停止歸並頁:
$ find pages-1246363747/0-286/ -type f -name '*.page' | sort -n | xargs cat > pages-1246363747/0-286/customer_pages_concatenated
生成的成果文件:pages-1246363747/0-286/customer_pages_concatenated,將作為constraints_parser對象的輸出。
6.2 運轉constraints_parser對象
上面到恢單數據最焦點的步調——運轉constraints_parser對象以提取行記載。和page_parser對象一樣,須要經由過程-5或-4參數指定InnoDB頁格局(COMPACT/REDUNDANT),-f指定輸出文件。
回到例子中,我們可以如許運轉constraints_parser對象(上面的敕令是恢復一個單一的頁,也能夠直接恢復經由6.1步調歸並一切頁以後的文件):
$ ./constraints_parser -5 -f pages-1246363747/0-286/50-00000050.page
輸入成果中每行包括表名和表中的各個列。備注:個中能夠有准確的行記載,也能夠有不准確的行記載。官方文檔中這個章節給出了若何調劑表界說獲得盡量多的有用數據,同時過濾失落渣滓行,這裡不再具體描寫。
customer 0 120 "" "" "" 32770 0 "0000-00-00 00:12:80" 0
customer 0 0 "" "" "" 0 0 "9120-22-48 29:44:00" 2
customer 61953 0 "" "" "" 2816 0 "7952-32-67 11:43:49" 0
customer 0 0 "" "" "" 0 0 "0000-00-00 00:00:00" 0
... snip ...
customer 0 0 "" "" "" 0 0 "0000-00-00 00:00:00" 16777728
customer 28262 114 "" "" NULL 25965 117 "4603-91-96 76:21:28" 5111809
customer 0 82 "" "" "" 22867 77 "2775-94-58 03:19:18" 1397573972
customer 2 1 "PATRICIA" "JOHNSON" "
[email protected]" 6 1 "2006-02-14 22:04:36" 1140008240
customer 3 1 "LINDA" "WILLIAMS" "
[email protected]" 7 1 "2006-02-14 22:04:36" 1140008240
customer 4 2 "BARBARA" "JONES" "
[email protected]" 8 1 "2006-02-14 22:04:36" 1140008240
customer 5 1 "ELIZABETH" "BROWN" "
[email protected]" 9 1 "2006-02-14 22:04:36" 1140008240
customer 6 2 "JENNIFER" "DAVIS" "
[email protected]" 10 1 "2006-02-14 22:04:36" 1140008240
customer 7 1 "MARIA" "MILLER" "
[email protected]" 11 1 "2006-02-14 22:04:36" 1140008240
customer 8 2 "SUSAN" "WILSON" "
[email protected]" 12 1 "2006-02-14 22:04:36" 1140008240
customer 9 2 "MARGARET" "MOORE" "
[email protected]" 13 1 "2006-02-14 22:04:36" 1140008240
... snip ...
customer 0 0 "" "" "" 0 0 "0000-00-00 00:00:00" 0
customer 0 0 "" "" "" 0 0 "7679-35-98 86:44:53" 720578985
7. 導入數據到數據庫中
最初,為了完成數據恢復,須要將步調6中constraints_parser對象的輸入成果,應用LOAD DATA INFILE敕令導入到數據庫中。敕令以下:
LOAD DATA INFILE '/tmp/customer_data.tsv'
REPLACE INTO TABLE customer
FIELDS TERMINATED BY '\t'
OPTIONALLY ENCLOSED BY '"'
LINES STARTING BY 'customer\t'
(customer_id, store_id, first_name, last_name, email,
address_id, active, create_date, @last_update)
SET last_update = FROM_UNIXTIME(@last_update);
至此,完成了數據的恢復和導入進程。願望年夜家不會無機會去理論這篇文章引見的辦法。