首页 数据库 mysql教程 MySQL数据库InnoDB数据恢复工具的使用小结详解_MySQL

MySQL数据库InnoDB数据恢复工具的使用小结详解_MySQL

Jun 01, 2016 pm 01:24 PM
数据库 数据恢复 记录

bitsCN.com 本文从实际使用经验出发,介绍一款开源的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是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"       "PATRICIA.JOHNSON@sakilacustomer.org"   6       1       "2006-02-14 22:04:36"   1140008240
customer        3       1       "LINDA" "WILLIAMS"      "LINDA.WILLIAMS@sakilacustomer.org"     7       1       "2006-02-14 22:04:36"   1140008240
customer        4       2       "BARBARA"       "JONES" "BARBARA.JONES@sakilacustomer.org"      8       1       "2006-02-14 22:04:36"   1140008240
customer        5       1       "ELIZABETH"     "BROWN" "ELIZABETH.BROWN@sakilacustomer.org"    9       1       "2006-02-14 22:04:36"   1140008240
customer        6       2       "JENNIFER"      "DAVIS" "JENNIFER.DAVIS@sakilacustomer.org"     10      1       "2006-02-14 22:04:36"   1140008240
customer        7       1       "MARIA" "MILLER"        "MARIA.MILLER@sakilacustomer.org"       11      1       "2006-02-14 22:04:36"   1140008240
customer        8       2       "SUSAN" "WILSON"        "SUSAN.WILSON@sakilacustomer.org"       12      1       "2006-02-14 22:04:36"   1140008240
customer        9       2       "MARGARET"      "MOORE" "MARGARET.MOORE@sakilacustomer.org"     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); 

至此,完成了数据的恢复和导入过程。希望大家不会有机会去实践这篇文章介绍的方法。
bitsCN.com

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前 By 尊渡假赌尊渡假赌尊渡假赌
仓库:如何复兴队友
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Go语言如何实现数据库的增删改查操作? Go语言如何实现数据库的增删改查操作? Mar 27, 2024 pm 09:39 PM

Go语言是一种高效、简洁且易于学习的编程语言,因其在并发编程和网络编程方面的优势而备受开发者青睐。在实际开发中,数据库操作是不可或缺的一部分,本文将介绍如何使用Go语言实现数据库的增删改查操作。在Go语言中,我们通常使用第三方库来操作数据库,比如常用的sql包、gorm等。这里以sql包为例介绍如何实现数据库的增删改查操作。假设我们使用的是MySQL数据库。

在PHP中使用MySQLi建立数据库连接的详尽教程 在PHP中使用MySQLi建立数据库连接的详尽教程 Jun 04, 2024 pm 01:42 PM

如何在PHP中使用MySQLi建立数据库连接:包含MySQLi扩展(require_once)创建连接函数(functionconnect_to_db)调用连接函数($conn=connect_to_db())执行查询($result=$conn->query())关闭连接($conn->close())

Hibernate 如何实现多态映射? Hibernate 如何实现多态映射? Apr 17, 2024 pm 12:09 PM

Hibernate多态映射可映射继承类到数据库,提供以下映射类型:joined-subclass:为子类创建单独表,包含父类所有列。table-per-class:为子类创建单独表,仅包含子类特有列。union-subclass:类似joined-subclass,但父类表联合所有子类列。

iOS 18 新增'已恢复”相册功能 可找回丢失或损坏的照片 iOS 18 新增'已恢复”相册功能 可找回丢失或损坏的照片 Jul 18, 2024 am 05:48 AM

苹果公司最新发布的iOS18、iPadOS18以及macOSSequoia系统为Photos应用增添了一项重要功能,旨在帮助用户轻松恢复因各种原因丢失或损坏的照片和视频。这项新功能在Photos应用的"工具"部分引入了一个名为"已恢复"的相册,当用户设备中存在未纳入其照片库的图片或视频时,该相册将自动显示。"已恢复"相册的出现为因数据库损坏、相机应用未正确保存至照片库或第三方应用管理照片库时照片和视频丢失提供了解决方案。用户只需简单几步

深入解析HTML如何读取数据库 深入解析HTML如何读取数据库 Apr 09, 2024 pm 12:36 PM

HTML无法直接读取数据库,但可以通过JavaScript和AJAX实现。其步骤包括建立数据库连接、发送查询、处理响应和更新页面。本文提供了利用JavaScript、AJAX和PHP来从MySQL数据库读取数据的实战示例,展示了如何在HTML页面中动态显示查询结果。该示例使用XMLHttpRequest建立数据库连接,发送查询并处理响应,从而将数据填充到页面元素中,实现了HTML读取数据库的功能。

MySQL数据库管理系统的基本原理解析 MySQL数据库管理系统的基本原理解析 Mar 25, 2024 pm 12:42 PM

MySQL数据库管理系统的基本原理解析MySQL是一种常用的关系型数据库管理系统,它通过结构化查询语言(SQL)来进行数据存储和管理。本文将介绍MySQL数据库管理系统的基本原理,包括数据库的创建、数据表的设计、数据的增删改查等操作,并提供具体的代码示例。一、数据库的创建在MySQL中,首先需要创建一个数据库实例来存储数据。通过以下代码可以创建一个名为"my

PHP处理数据库中文乱码的技巧与实践 PHP处理数据库中文乱码的技巧与实践 Mar 27, 2024 pm 05:21 PM

PHP是一种广泛应用于网站开发的后端编程语言,它具有强大的数据库操作功能,常用于与MySQL等数据库进行交互。然而,由于中文字符编码的复杂性,在处理数据库中文乱码时常常会出现问题。本文将介绍PHP处理数据库中文乱码的技巧与实践,包括常见的乱码原因、解决方法和具体的代码示例。常见的乱码原因数据库字符集设置不正确:数据库在创建时需选择正确的字符集,如utf8或u

如何在PHP中处理数据库连接错误 如何在PHP中处理数据库连接错误 Jun 05, 2024 pm 02:16 PM

PHP中处理数据库连接报错,可以使用以下步骤:使用mysqli_connect_errno()获取错误代码。使用mysqli_connect_error()获取错误消息。通过捕获并记录这些错误信息,可以轻松识别并解决数据库连接问题,确保应用程序的顺畅运行。

See all articles