记录一次手残删除mysql中ibdata1中相关文件通过仅存的.frm和.ibd恢复数据库数据历史

前几天无意间去打开零食小铺站点(https://snackshop.lby.link) 发现mysql报错了,页面上大概的意思就是无法连接到mysql,一看这个节奏,心想还以为是磁盘空间满了导致mysql启动失败,因为之前有这个前例。接着打开ssh 终端登录阿里云的ecs,看了下磁盘空间正常,不是空间不足导致启动不了,然后尝试手动启动mysql:

service mysqld restart

依然无果还是报错的,正常思路应该是找找mysql日志,查看一下日志然后根据日志分支本次启动失败的原因。

可能搬砖太累了回来没在状态,不知道哪里误操作了一下,把mysql下的ibdata 及相关的在一起的几个文件一起删了,后面还重试重启了mysql服务,到这基本上ibdata文件已经没有可能恢复了,因为找不到了,如果在没有重启的前提下还是可以找回的。(如果有使用一些恢复工具可能有找回,不过比较折腾,而且在重启mysql的服务后生成了新的ibdata),好吧到此故事前提已经结束,接下来就说说怎么折腾回去吧,oh my god! 我困 ~ (下班回来吃晚饭已经9点多了,本以为一会能搞定的现在10多点了...)

目前存有的数据是mysql数据文件夹下的数据库名目录下的.frm 和 .ibd文件 ,本次记录就是讲讲怎么通过这两个文件恢复数据库。

.frm 文件:存储的是数据表结构相关元数据 通过.frm 可以恢复表结构(当然不是直接恢复,需要操作一波)

.ibd 文件:单个表中的表数据

理论上,有上面两个文件(.frm和.ibd)就可以恢复一个数据库及数据了。理论通过,接下来开始实践(涉及到其他没有说明的理论就不在这里说了,这次只讲实践)


开始恢复

恢复前需要创建一个与需要恢复数据库同名的新数据库(必须同名) 可以把保留下的.frm 和 .ibd 文件备份一下,以免操作失误。

需要的工具

这次是直接把文件下载到本地windows环境恢复的(主要是图形化方便一点,等恢复后,可以导出再导入到服务器端的数据库)

1.mysql 管理工具:SQLyog 或者phpmyAdmin , navicat
2.git bash (用cmd也可以 ,只是好看方便一点)
3.windows 版的 mysql-utilities-1.6.5-winx64.msi [下载链接](https://downloads.mysql.com/archives/utilities/)
安装完上面的工具之后,可以使用SQLyog 连接上数据库,创建一个需要恢复的同名数据库,这里使用:【mywebsite】

1.恢复表和表结构

通过mysql-utilities工具(需要提前安装好,在命令行终端使用,安装后可以直接使用mysqlfrm 命令恢复表结构)从对应的.frm文件恢复数据表的创建SQL。(一张表对应一个.frm文件 和 .ibd文件)

在cmd命令行工具或git bash终端中输入:

// --diagnostic 直接分析
// barragers.frm 需要恢复表的.frm文件路径
mysqlfrm --diagnostic barragers.frm

执行完上面这个命令,如果没有意外应该可以得到类似如下的内容:

# WARNING: Cannot generate character set or collation names without the --server option.
# CAUTION: The diagnostic mode is a best-effort parse of the .frm file. As such, it may not identify all of the components of the table correctly. This is especially true for damaged files. It will also not read the default values for the columns and the resulting statement may not be syntactically correct.
# Reading .frm file for barragers.frm:
# The .frm file is a TABLE.
# CREATE TABLE Statement:

CREATE TABLE `barragers` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(60) DEFAULT NULL,
  `type` varchar(60) DEFAULT NULL,
  `p` varchar(60) DEFAULT NULL,
  `time` varchar(60) DEFAULT NULL,
  `ip` varchar(60) DEFAULT NULL,
PRIMARY KEY `PRIMARY` (`id`)
) ENGINE=InnoDB;

#...done.
此时已经分析得出了CREATE 的SQL ,可以直接复制出来到前面已经创建好的数据库下执行该SQL,从而创建出一张空表,可以自行确认表结构是否正常(可能会出现一些表字段配置丢失的可能,因为可能执行不成功语法错误,需要稍微修改一下分析出来的SQL)

2.恢复表数据:

在成功创建一张表之后,可以打开mysql 数据库的data目录下,找到数据库同名目录,发现里面成功创建出(barragers.frm 和 barragers.ibd 两个文件) 数据本身是存在.ibd文件中【不能直接删除barragers.ibd,使用备份的替换】 按以下步骤操作:

1.删除新建的表空间

// 执行完该SQL后,mysql数据目录下的 barragers.ibd 会被删除
ALTER TABLE mywebsite.barragers DISCARD TABLESPACE;

2.将之前备份的barragers.ibd文件拷贝到mysql目录下

3.接下来就是关联表空间(可以理解为把备份的.ibd文件和前面创建表结构的.frm绑定)

ALTER TABLE mywebsite.barragers IMPORT TABLESPACE;
如果有幸成功一次导入成功,那么恭喜你,第一张数据已经恢复,但如果失败有可能是以下原因:

1.在linux中操作很多时候就是因为拷贝进去的.ibd 文件没有执行权限,需要设置相关写入权限 错误如下:

Error Code:1812. Tablespace is missing for table mywebsite.barragers

2.通过解析出来的CREATE创建的表的ROW_FORMAT 和 .ibd文件的 ROW_FORMAT 格式不一致,会造成无法导入。 报错如下:

Error Code: 1808. Schema mismatch (Table has ROW_TYPE_DYNAMIC row format, mywebsite.barragers.ibd file has ROW_TYPE_COMPACT row format.)

解决方案:可以直接把表的ROW_TYPE 设置跟.ibd格式一样,按上面的提示是表的格式是【DYNAMIC】,而.ibd文件的格式是【COMPACT】,那可以在数据库中执行以下SQL:

ALTER TABLE `mywebsite`.`barragers` ROW_FORMAT =COMPACT;

// 把表的格式设置跟.ibd文件一样,可能实际情况格式不一定只有这两种,意思是一样哈。

这一次我一共恢复了32张表,因为不想折腾太久,想着再次出现这种情况的几率也不大,就不再自己造轮子写脚本,全手动一张张恢复,只要第一张恢复出来了,后面的很快就搞定了,最后验证的时候还是发现出了的小问题,就是一些表数据结构的问题,这个可能再定位调试一下。