欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

MySQL Innodb数据库误删ibdata1后MySQL数据库的恢复案例

程序员文章站 2023-01-12 13:35:30
上周,以前公司的同事朋友找我帮忙,看看能否帮忙恢复一个MySQL 数据库,具体情况为:数据库版本为MySQL 5.6(具体版本不清楚),也不清楚具体的数据库引擎; 没有数据库备份,只剩下数据库下面的一些文件(frm、idb),具体原因是因为出现问题的时候,重装了MySQL,最要命的是ibdata1等... ......

 

上周,以前公司的同事朋友找我帮忙,看看能否帮忙恢复一个mysql 数据库,具体情况为:数据库版本为mysql 5.6(具体版本不清楚),也不清楚具体的数据库引擎; 没有数据库备份,只剩下数据库下面的一些文件(frm、idb),具体原因是因为出现问题的时候,重装了mysql,最要命的是ibdata1等文件也没有了,当然这中间细节过程如何,不清楚也不用去纠结了。大概就是这么一个情况。

 

 

因为数据库不大,将对应的文件拷贝到自己一台测试服务器的mysql数据文件目录下后(下面实验测试,对数据库名等敏感信息做了一下混淆),如下所示,数据库名为test,show tables可以看到相关的表。

 

 

 

 

其中有几张表的存储引擎为myisam,那么这些表的数据是完全可以恢复的,但是大部分表的存储引擎为innodb,访问表或查看表都会提示error 1146 (42s02): table 'xxxx' doesn't exist 不存在。

 

mysql> desc think_cache;
error 1146 (42s02): table 'test.think_cache' doesn't exist
mysql> show create table think_cache;
error 1146 (42s02): table 'test.think_cache' doesn't exist
mysql> 

 

由于共享表空间的ibdata1数据文件不存在了,加之有没有备份,所以我武断的判断这个数据库真的无法恢复了,但是过后一天,这个朋友跟我说找了一家数据恢复公司将这个数据库恢复了。 听到这个消息颇有点学艺不精的尴尬(其实谈不上尴尬吧,本来还在学习mysql的路上,有些知识点不清楚也很正常。经验是需要慢慢积累的),不过更多的是好奇别人是如何恢复数据的,既然别人能够恢复,那么自己下一次遇到这种情况也要能搞定。下面就来复盘一下别人是如何恢复数据的(其实只要稍稍做点功课,发现这个其实挺简单的)

 

首先,我们来了解一下mysql 表空间数据文件idbdat1文件相关概念和知识点:

 

    innodb采用按表空间(tablespace)的方式进行存储数据, 默认配置情况下会有一个初始大小为10mb, 名字为ibdata1的文件, 该文件就是默认的表空间文件(tablespce file),用户可以通过参数innodb_data_file_path对其进行设置,可以有多个数据文件,如果没有设置innodb_file_per_table的话, 那些innodb存储类型的表的数据都放在这个共享表空间中,而系统变量innodb_file_per_table=1的话,那么innodb存储引擎类型的表就会产生一个独立表空间,独立表空间的命名规则为:表名.idb. 这些单独的表空间文件仅存储该表的数据、索引和插入缓冲bitmap等信息,其它信息还是存放在共享表空间中。

 

    其实当时主要是对这个概念有点模糊了,以为这个系统变量innodb_file_per_table默认是关闭的,数据都会存储在共享表空间中,那么这些文件删除了,数据就无法恢复。所以武断的下结论,其实从mysql 5.6.6开始, 系统变量innodb_file_per_table默认是启用的。只要再多了解一点或者说更深入了解一点的话,情况就会立马就会反转。也就是说如果开启了独立表空间,可从ibd文件中恢复数据。即使共享表空间的数据文件idbdata1丢失也不要紧,反之,如果未开启独立表空间时,idbdat1被删除了,数据也会被删除,只能从备份中恢复,真的没有其他办法。

 

 

那么我们接下来看看,如何从idb文件中恢复数据吧,我们需要用到mysqlfrm工具, 需要安装mysql utilities,下面是安装mysql utilities 1.5.5

 

# tar -xvf mysql-utilities-1.5.5.tar.gz

# cd mysql-utilities-1.5.5

# python ./setup.py build

# python ./setup.py install

 

 

提取frm文件的表结构信息

 

mysqlfrm 是一个恢复性质的工具,用来读取.frm文件并从该文件中找到表定义数据生成create语句。此处不对mysqlfrm工具做过多介绍,我们使用msqlfrm来生成该数据库的表的create语句

 

[root@db-server ~]# service mysql stop
shutting down mysql.... success! 
[root@db-server ~]# /usr/local/bin/mysqlfrm --basedir=/usr --port=3306 --user=root /data/mysql/test/ > test_frm.sql
[root@db-server ~]# 

 

检查导出的sql语句发现都是error: the server version for this file is too low. it requires a server version 5.6.29 or higher but your server is version 5.6.20. try using a newer server or use diagnostic mode这类错误

 

[root@db-server ~]# more test_frm.sql 
# spawning server with --user=root.
# starting the spawned server on port 3306 ... done.
# reading .frm files
#
# reading the think_cache.frm file.
error: the server version for this file is too low. it requires a server version 5.6.29 or higher but your server is version 5.6.20. try using a newer server or use diagnostic mode.
#
# reading the think_session.frm file.
error: the server version for this file is too low. it requires a server version 5.6.29 or higher but your server is version 5.6.20. try using a newer server or use diagnostic mode.
#
# reading the wx_activity_config.frm file.
error: the server version for this file is too low. it requires a server version 5.6.29 or higher but your server is version 5.6.20. try using a newer server or use diagnostic mode.
#
........................................................................................

 

从中可以看到这个数据库之前的版本为mysql5.6.29而我这里的mysql版本比这个低mysql 5.6.20。所以必须找一个跟这个版本相同或高的mysql数据库操作才行。于是在另外一台测试服务器安装了mysql

 

[root@gettestlnx02 ~]# service mysqld stop
 
stopping mysqld:  [  ok  ]
 
[root@gettestlnx02 tmp]# mv test  /data/mysqldata/mysql/test
 
[root@gettestlnx02 tmp]# cd /data/mysqldata/mysql/

 

/usr/bin/mysqlfrm --basedir=/usr --port=3306 --user=root /data/mysqldata/mysql/test/ > test_frm.sql

 

如何要查看输出信息,可以使用参数-vvv

 

/usr/bin/mysqlfrm --basedir=/usr --port=3306 --user=root -vvv /data/mysqldata/mysql/test/ > test_frm.sql

 

 

生成的sql脚本没有以分号结尾,本来想用sed命令给那些create table脚本加上分号结尾,但是发现其中大量create table的脚本结尾没有规律,都是以commnet='xxxxx'结尾,只能手工添加分号(如下所示)

 

 

 

 

导入frm文件的表结构信息

 

mysql> use test;
database changed
mysql> source test_frm.sql
query ok, 0 rows affected (0.02 sec)
 
query ok, 0 rows affected (0.01 sec)
 
query ok, 0 rows affected (0.01 sec)
 
query ok, 0 rows affected (0.00 sec)
.................................

 

然后我们检查这个数据库的各类文件frm、ibd、myi、myd文件数量,后续做对比验证用途。

 

[root@gettestlnx02 test]# ls -lrt *.frm | wc -l

84

[root@gettestlnx02 test]# ls -lrt *.ibd | wc -l  

84

[root@gettestlnx02 test]# ls -lrt *.myi | wc -l     

22

[root@gettestlnx02 test]# ls -lrt *.myd | wc -l 

22

[root@gettestlnx02 test]#

 

 

删除新建表的独立表空间文件

 

使用下面脚本生成删除新建表的独立空间的脚本:

 

select concat(concat('alter table ',table_name), ' discard tablespace;')                                                  
from information_schema.tables                               
where table_schema='test' and engine ='innodb';

 

使用脚本就可以生成下面sql,执行该命令后,对应数据库下面的ibd文件全部被删除。

 

alter table think_cache discard tablespace;      

alter table think_session discard tablespace;    

alter table wx_activity_config discard tablespace;

........................................

 

 

 

复制待恢复的表空间文件

 

将待恢复的ibd文件拷贝到对应数据库目录下面,并设置好权限属性

 

# cd /tmp/database
# ls -lrt *.ibd | wc -l
84
# cp *.ibd /var/lib/mysql/test
 
# chown  mysql:mysql *.ibd
# chmod 660 *.ibd

 

 

导入表空