记一次灾难性的mysql数据恢复
2018年5月21日,吃饱了没事干,想搞邮件服务器,百度找到个叫U-mail的东西。
进了官网,感觉挺好,脑子抽了,直接生产环境上装了,发现很垃圾,卸载。
rpm一看,包很多,写了个脚本一键删了。
删完重启,心想生产环境不会被他弄坏吧。一看wp,完蛋了,打不开了。
看了下本地服务器上的数据库备份。发现最新的备份是3月8号的。
我4,5月份写了那么多笔记啊!!!!!!!
开始想办法恢复数据。
systemstl status mysql
发现mysql这个服务都不存在了,完蛋。
cd /usr/local/mysql
看一下物理目录还在吗,能进去,还在。
数据库的数据文件一般保存在/usr/local/mysql/var/数据库名字 内,进去看了一下,文件都在。
.idb格式的是数据内容 .frm格式的是数据结构
现在要做的就是把这些东西恢复成数据库的数据。
换了台机子,重新装了mysql同版本,进了/usr/local/mysql/var 把原机子上的数据库源文件复制进去,重启数据库,数据库直接报错。
网上查了下,说是改一下/etc/my.cnf文件,添加innodb_force_recovery=1-6 就可以了。试了一遍,不行。痛苦。
后来问了下猫叔。猫叔给了个链接,并祝我好运。
https://www.cnblogs.com/xj2015/p/7479999.html
简述一下操作流程。。。
首先查看一下/etc/my.cnf中的innodb_file_per_table值是不是1
如果是1的话有可能用自动化工具修好,不是1的话只能手动修复。
看了下我的,发现是1,试一下自动工具。
此工具需要Windows环境 .net4.x版本、MySQL5.6的最新版本。
mysql5.6.40 http://125.71.5.35:2333/index.php?share/file&user=1&sid=bagrDdQw
工具使用方式:
InnoDBRestore
例如
InnoDBRestore root pass 3306 c:\dbcopy my_database
祝好运吧。如果运气好,无报错, c:\dbcopy 下的MyISAM和InnoDB数据都会被导入 my_database(my_database不需要提前创建)。
然而你可能会和我一样,遇上了数据错误(天知道为什么),导入过程可能会报错。这些报错很可能是连接被关闭
restoring : wp_posts.frm
unknown error:MySql.Data.MySqlClient.MySqlException (0x80004005): Fatal error encountered during command execution. —> MySql.Data.MySqlClient.MySqlException (0x80004005): Fatal error encountered attempting to read the resultset. —> MySql.Data.MySqlClient.MySqlException (0x80004005): Reading from the stream has failed. —> System.IO.IOException: 无法从传输连接中读取数据: 远程主机强迫关闭了一个现有的连接。。 —> System.Net.Sockets.SocketException: 远程主机强迫关闭了一个现有的连接。
然后同时,查看数据库的err日志,可能会有类似以下报错:
InnoDB: Error: trying to access page number 1372160 in space 1, InnoDB: space name recovery1/wp_posts, InnoDB: which is outside the tablespace bounds. InnoDB: Byte offset 0, len 16384, i/o type 10. InnoDB: If you get this error at mysqld startup, please check that InnoDB: your my.cnf matches the ibdata files that you have in the InnoDB: MySQL server.
由于InnoDB引擎遇上了异常,MySQL崩溃退出,导致连接断开。这种情况下,到MySQL的data目录下,删除刚才导入的数据库的文件夹以及ib_logfile0、ib_logfile1、ibdata1(也就是重置所有InnoDB引擎相关数据)。然后再启动MySQL。
将引发故障的表文件(ibd、frm)单独移出来,留作阶段2修复使用,再次执行InnoDBRestore,如再遇上故障重复以上步骤,直到工具不再报错为止。
使用mysqldump将表导出来。建议添加–skip-extended-insert参数以便数据检查,如果上面一切都顺利,无论是导入还是导出都没有任何报错,也需要仔细检查恢复出来的数据是否有异常(很大的负数、数据参杂乱码、不合理的日期等),有些情况下会有隐性损坏情况。如果没有,那么恭喜你数据就恢复完成了,不需要继续向下阅读了
……然而你可能像我一样,导出时再次遇上相似故障
ERROR 2013 (HY000): Lost connection to MySQL server during query
查看err日志后,发现另一个原先看起来成功导入的ibd文件dump时由于数据错误也失败了,修改my.cnf,添加innodb_force_recovery=6。然后再重新启动,再次尝试dump查看是否成功,如成功需要仔细检查是否数据正确。如果连 innodb_force_recovery=6 也无法获得正确数据的话,只能跳过出故障的表,将其余正常的表导出。出故障的表通过第二阶段进行修复。
总的来说,我wp_posts这张表挂了,无法用自动工具修复,在数据库中想要查看这张表,数据库就会崩溃。
只能尝试手动修复这张表了。
要用到工具https://github.com/chhabhaiya/undrop-for-innodb 并且要在有mysql环境的linux上运行。
安装undrop-for-innodb工具,只需要执行make命令进行编译,很简单也非常快。
该工具可用于很多 InnoDB 灾难性数据丢失场景的数据库救援。救援的意思是尽量恢复数据,通常需要这个工具的场合都是很糟糕的,运气好的情况下你或许能全部提取出。因此无论如何依然不能直接拷贝InnoDB数据库。p.s.今年1月此工具停止进一步开发了,很可惜
make编译后会在其目录生成以下可执行工具:
c_parser innochecksum_changer stream_parser
1.使用
./stream_parser -f wp_posts.ibd
拆出ibd文件结构
2.使用mysqlfrm拆出包含表结构的CREATE TABLE语句,在第一阶段 zcgonvh的工具里有一个Windows的MySqlFrm.exe亦可使用,这里以该工具为例。Linux的 mysqlfrm可以参考下面本文后杂记
mysqlfrm
例如: mysqlfrm root pass 3306 c:\dbcopy
会在同目录下对每个frm文件生成一个.sql文件 内含创建表语句。注意该工具生成的CREATE TABLE语句不含分号,会对之后操作造成影响,需要在语句末尾添加一个分号
3.拆出的ibd文件结构会存储在pages-wp_posts.ibd里。包含以下子目录:
FIL_PAGE_INDEX:一般PAGE,依照其ID存放
FIL_PAGE_TYPE_BLOB: 如果遇上较大的数据(例如comments里有text类型的数据并且内容较多),InnoDB会使用BLOB类PAGE存储数据。需检查此目录是否有文件。如有,说明此表使用了BLOB,之后提取命令需要用-b参数指定此目录进行提取
按照步骤1拆分ibdata1,然后编辑recover_dictionary.sh脚本里的mysql命令行 在后面加上 -u root -p密码
然后执行此脚本,会将SYS系列表导入test数据库
使用mysql命令行进入test数据库后,执行:
mysql> select * from SYS_TABLES where NAME like “%/wp_posts”;
此table的ID为116,然后执行
mysql> SELECT * FROM SYS_INDEXES where table_id=116;
即可获得主键的index_id为218,因此对应的page是:pages-wp_posts.ibd/FIL_PAGE_INDEX/0000000000000218.page。
如果没有ibddata1的情况下就没法用工具判断id了,只能一个一个猜了
使用c_parser命令对每个page尝试提取。此表数据结构是COMPACT,因而使用参数-5。如果是MYSQL5.6以上的格式用-6。不确定的话5和6都试下
本例BLOB目录下有文件,需要-b参数指定BLOB目录以确保数据完整。
步骤2得到的表结构定义(CREATE TABLE)放在 wp_posts.frm.sql
将输出指向到less以便阅读:
[root@Test undrop-for-innodb]#./c_parser -5f ./pages-wp_posts.ibd/FIL_PAGE_INDEX/XXXXX.page -b ./pages-wp_posts.ibd/FIL_PAGE_TYPE_BLOB/ -t ./wp_posts.frm.sql | less
XXXXX替换成具体的page编号,建议从头开始尝试(对两个有类似问题的表修复的结果似乎暗示第一个的成功概率最高),直到获得了明显正确的结果(日期正确 大部分数据正常 Records list为Valid)
c_parser会将tsv数据dump到标准输出管道,并且很贴心的将对应数据恢复SQL命令特意单独输出到了错误输出(某个N年未更新的工具还需要自行构建命令导入)。可以使用以下命令对含有正确数据索引的page做最终导出:
./c_parser -5f pages-wp_posts.ibd/FIL_PAGE_INDEX/0000000000224178.page -b pages-wp_posts.ibd/FIL_PAGE_TYPE_BLOB/ -t wp_posts.frm.sql > wp_posts 2> wp_posts.sql
会得到
wp_posts:提取出来的tsv格式数据 wp_posts.sql:将tsv导入数据库的SQL命令
编辑器打开这两个文件,可以看到wp_posts.sql实际上保存了原先数据表的结构
wp_posts则是保存了具体数据。
我们需要将这两个文件挪到/tmp文件夹(规避权限问题),然后修改 wp_posts.sql 内的LOAD DATA LOCAL INFILE路径,使之符合新的tsv文件路径。
之后要做的就很简单了
新建一个和原先表结构完全一样的表,然后SOURCE /tmp/wp_posts.sql;就能将数据导进数据库了。
最后dump出来,就是标准的SQL格式了。
能正常访问了!
弄完一想,如果以后渗透的时候,机子权限有了,但是没数据库权限,可以把idb文件和frm文件搞下来,然后离线脱裤!不错不错:)