mysql8.0无备份通过idb文件恢复数据过程、idb文件修复和tablespace id不一致处理

周末突然接到一位一年多没联系的妹妹打来电话,"刘哥,快来救救我",我脑海瞬间冒出妙瓦底,电信火苲马扁.....,当时就冒汗了,心想这个妹子怎么被...

问其原由,原来是他们公司服务器掉电,重启后单位的站点打不开了,请求支援...

妹妹说搞定请我吃临沂炒鸡,作为从业N年的码农,英雄救美义不容辞。立即驱车18公里,火速前往事故现场。

经了解项目tomcat服务启动正常,但是数据库没有正常启动导致web应用无法访问,数据数版本mysql8.0.26,这个没玩过咋整,平时都是弄mssql,oracle。

百度查阅各种资料,历时1天的时间,下面简单记录整个修复过程。

1、查看错误日志信息,日志文件在mysql数据目录中 C:\ProgramData\MySQL\MySQL Server 8.0\Data,文件命名格式为 计算机名称.err,本地文件liuxiaoxiao.err

信息显示: file not found liuxiaoxiao-bin.000031

此问题重命名data目录中liuxiaoxiao-bin.index文件为其他名称,或删除此文件。重启数据库服务后数据库正常启动,项目可以访问。


2、很开心,美女说还有一台也出故障,是以前的一个老项目不怎么用,但是历史数据偶尔查询,心想如法炮制不就ok了吗,一招制敌简单,哈哈

打开错误日志看到的信息完全不一样,错误信息显示mysqld got exception 0x80000003

11:38:26 UTC - mysqld got exception 0x80000003 ;

Most likely, you have hit a bug, but this error can also be caused by malfunctioning hardware.

Thread pointer: 0x2ce9279b3f0

Attempting backtrace. You can use the following information to find out

where mysqld died. If you see no messages after this, something went

terribly wrong...

7ff70802a1a2 mysqld.exe!?my_errno@@YAHXZ()

7ff970f3e5f5 ucrtbase.dll!raise()

7ff970f3f601 ucrtbase.dll!abort()

此处省略若干行......

7ff970ee9363 ucrtbase.dll!_recalloc()

7ff97285257d KERNEL32.DLL!BaseThreadInitThunk()

7ff9738caa58 ntdll.dll!RtlUserThreadStart()

3、查阅资料尝试操作,对Data目录进行重新初始化,可以正常启动数据库服务,但是复制备份文件过去后服务启动仍然异常,过程如下。

参考文章:https://blog.csdn.net/ddd295569371/article/details/125329457

https://blog.csdn.net/ddd295569371/article/details/125330919?spm=1001.2014.3001.5501

1)、备份整个data目录,现场电脑的路径是C:\ProgramData\MySQL\MySQL Server 8.0\Data,然后清空Data目录

2)、cmd 运行C:\Program Files\MySQL\MySQL Server 8.0\bin目录下的mysqld.exe 执行以下命令mysqld --initialize回车等待完成

初始化完成,服务启动让提示错误,这里操作完成后查看data目录并没有产生新文件,结果初始化后Data目录跑到C:\Program Files\MySQL\MySQL Server 8.0\Data目录,手动将初始化产生的文件复制到C:\ProgramData\MySQL\MySQL Server 8.0\Data下,启动服务成功。

根据文章指导,初始化后root的新密码在错误日志文件中xxxxx.err获取,修改root密码为原密码

C:\Program Files\MySQL\MySQL Server 8.0\bin>mysql -u root -P 3306 -p

mysql> alter user 'root'@'localhost' identified by '123xxx' password expire never;

mysql> alter user 'root'@'localhost' identified with mysql_native_password by '123xxx';

mysql> flush privileges;

根据文章提示停止mysql服务,复制原Data备份目录的ibdata1、ib_logfile1、ib_logfile0、ib_buffer_pool、auto.cnf、 mysql.ibd到新产生的Data下,再重启服务失败,查看错误日志无任何日志输出,而且服务启动提示失败,查看window事件日志也没有任何可用信息。

使用记事本修改my.ini数据库配置文件在[mysqld]下增加innodb_force_recovery=1配置信息,仍然启动失败

重新初始化Data目录,删除上面改动添加的innodb_force_recovery=1配置信息,启动提示同样错误,提示大致信息:服务依赖的xxxx停止。

截至目前情况变的更糟糕了,初始化后正常的服务都不能启动,本身就对mysql不太了解,心里开始发懵,决定尝试卸载重新安装mysql,版本必须保持与原来安装版本一直,因为后面还要进行数据库修复。

4、重新安装数据库:

卸载数据库,删除C:\ProgramData\MySQL\;C:\Program Files\MySQL\ 两个目录下的MySQL Server 8.0文件夹、(注意:原来Data目录备份保留好),重新安装数据库服务正常启动,备份my.ini配制文件,采取逐步尝试的方法发现,my.ini文件修改后,服务就启动不起来,猜测修改配制异常导致的,恢复my.ini的备份使用notepad++修改配制文件后可以正常启动服务。

小总结:mysql数据库配置文件my.ini不能使用记事本编辑,这个会导致文件编码异常进而导致服务不能启动。这个要注意!!!

重复上述操作复制ibdata1、ib_logfile1、ib_logfile0...等文件到新目录,服务启动失败。

关键一步:仅复制Data备份mysql.ibd文件到现有目录,数据库服务启动正常,且使用Navicat链接数据库可以看到数据库,使用Navicat管理工具在数据库上鼠标右键|转储SQL文件|仅结构,顺利将整个库的建表语句生成,当时开心坏了...,猜想mysql.idb文件包含了每个数据库对象的信息。(此步记不清当时是否已将项目数据库目录文件放到Data中了,印象中记得是没有)

5、表修复和恢复数据库

1)、通过执行上步获取的脚本,重新创建数据库,这样就获取了一个空白的项目数据库。

2)、执行sql丢弃表空间,每个表都需要操作一遍:alter table cms_xxx discard tablespace; 其中cms_xxx是表名

3)、停止mysql服务复制备份目录项目数据库的idb文件替换覆盖第一步脚本创建生成的文件。

4)、执行sql导入表空间,每个表都需要操作一遍:alter table cms_xxx import tablespace; 其中cms_xxx是表名

执行不报错误的话,这个表就正常可以访问了。

当前项目数据库共77个表,执行过程中62张是可以导入表空间正常了,其中还有5张导入失败,其中3张是log_开头的,估计是操作日志类的表,没有太大业务关系,这个3张表直接新建数据不管了,另外一张是一个基础档案表数据不多,新建让重新维护记录就好了。

**  5)、麻烦的是其中最大的一张表200多兆,不能正常导入,而且根据名称推断这个是个关键的业务表。**网络搜索相关资料,最终恢复了大部分数据,仍有部分数据丢失。

总结具体操作步骤如下,恢复过程进行要损坏表对应的idb文件就行:

a、安装全新的mysql环境安装路径和服务器原来的路径要相同(或者在现有的mysql环境下操作也行),创建相同编码排序规则的同名数据库,创建同名的表名称。

b、停止服务,剪切a步骤新建表Data目录产生的.idb文件到其他任意自己的备份目录,将损坏的idb文件放到Data目录中,my.ini添加innodb_force_recovery=1配置信息,启动数据库服务,然后尝试查询这张数据表,数据库会报错或直接服务停掉了,此时查看错误日志,获取损坏文件的tablespace id值。

错误日志会看到大致如此信息:

复制代码
[ERROR] InnoDB: Error: tablespace id in file ‘.\db_web\cms_content_detail.ibd’ is 233, but in the InnoDB InnoDB: data dictionary it is 109

这是因为数据库系统记忆此表的表空间id和idb文件头里保存的表空间id不一致导致,(期间还发现mysql在启动的时候会访问数据库目录中的每个idb文件,如果库文件目录中将任意一个idb文件复制副本,启动mysql服务的时候,日期会提示重复的表空间文件)

这里233十进制转换16进制数据为E9,通过UltraEdit打开idb文件查看,验证了此结果,之所以有两处,是因为idb文件在文件头和page区的头部都保存了此信息。

接下来我们需要在此数据库上不停建立新表,每创建一个新表mysql系统内的tablespace id就会增加1,直到创建出来的新表文件这个ID值是E8,再通过建表语句创建待修复的表cms_content_detail,如此新产生的表对象的表空间ID就和原来坏掉的保持一致了,如何盘点新创建的表到E8(十进制232)呢,还是用UltraEdit(或其他16进制查看文件的工具)打开idb文件查看就可以。这里我是通过重复执行固定次数的建表tb1达到需要的目的( DROP table tb1;CREATE table tb1(id int); )

启动数据库服务,cmd命令窗口使用mysqldump导出备份数据(注意:mysqldump命令需要在mysql的bin目录执行,否则可能会提示无效命令)

mysqldump -u root -P 3306 -p db_web cms_content_detail > cms_tb01.sql 回车,输入root密码回车等待导出语句完成,cmsfix_01.sql文件包含了修复表的create、insert语句。

这里我是将修复级别改为6,即innodb_force_recovery=6才完成了数据导出的,逐个尝试1-5并不能完成修复,在6这个级别下最终还是有部分数据丢失没能找回,使用UltraEdit查看idb文件尾部都是000000空白内容(如果你的mysql占用的不是默认3306端口,可以使用-P 3307指定端口号的方式连接数据库)。

有了sql脚本,通过执行该脚本就可以重建表了,将mysql配置文件my.ini文件内的innodb_force_recovery配置项注释掉(否则数据库处于只读状态,没法写入数据)启动数据库,删除损坏的表,执行导出的sql脚本,执行语句格式:mysql -u<username> -p<password> <database> < <table_name>.sql

这里执行:mysql -u root -p db_web < cms_tb01.sql 回车等待执行完成,查看恢复表数据的情况。

系统复线后,添加内容提示错误,查看tomcat日志发现提示ID主键冲突,估计是数据库损坏导致新增序列ID值与已有数据库记录的ID重复,通过上面的mysqldump导出整个库,重新建库导入后正常(这样表的序列和索引都会被重建)至此整个修复过程结束,妹妹开心的不得了,"幸好有你"。

修复过程参考了此文章 https://www.cnblogs.com/jiangxu67/p/4744283.html,感谢以上网络资料博主的分享

--->>>期间还走了其他很多弯路,比如通过idb文件逆向生成建表语句,修改表数据库引擎类型,尝试其他修复语句等等,不唠叨了...