程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> MYSQL數據庫 >> MySQL綜合教程 >> MySQL數據庫InnoDB數據恢停工具的應用小結詳解

MySQL數據庫InnoDB數據恢停工具的應用小結詳解

編輯:MySQL綜合教程

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); 

至此,完成了數據的恢復和導入進程。願望年夜家不會無機會去理論這篇文章引見的辦法。
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved