基于Data目录恢复MySQL数据库运行

基于Data目录恢复MySQL数据库运行

1月 26, 2024

文章背景

记录一次因服务器阵列卡故障导致MySQL数据二进制日志损坏无法运行,并且服务器重启之后无法识别引导设备,无法启动系统,最终找回MySQL的data目录数据并恢复运行。

最近,公司数据库服务器突然无法访问了,用ssh也连不上,怀疑是服务器宕机了,然后去机房查看,发现服务器没有宕机,但是数据库日志一直在报错,如下:

通过报错日志,可以看出是无法读取数据。心想可能是磁盘有坏道或者是raid阵列卡有问题导致的,然后我就尝试重启服务器(这里大家不要学我,应该要先备份数),不曾想重启之后系统无法启动了,开机根本就识别不到引导设备,而检查Raid设备是正常的。

恢复方案

因为项目还没上线,还没有有做数据库备份,所以只能先将MySQL的data目录数据找回,然后基于其重新部署恢复。

找回数据

只能先做个引导U盘,用U盘Recovery模式启动之后发现,原来是系统分区没有了,但数据分区还在。这样就直接data目录拷贝出来,然后放到到另一台服务器上,基于data目录恢复即可。

这里我是比较幸运的,数据分区没丢,还能够将MySQL的data目录数据拷贝出来。如果不幸数据丢失,则借助分区数据恢复工具找回。以 extundelete 为例。

安装extundelete

wget -P /usr/local/src https://sourceforge.net/projects/extundelete/files/latest/download –no-check-certificate sdafsdavsasadadsa
tar -C /usr/local/src -xf /usr/local/src/extundelete-0.2.4.tar.bz2
cd /usr/local/src/extundelete-0.2.4/
./configure –prefix=/usr/local/extundelete
make && make install
cat > /etc/profile.d/extundelete.sh << EOF
export EXTUND_HOME=/usr/local/extundelete
export PATH=$PATH:$EXTUND_HOME/bin EOF
source /etc/profile.d/extundelete.sh && extundelete -v    # 检查是否安装成功

恢复数据

a、首先关闭所有读写此分区设备的程序,然后把目标分区卸载下来
b、extundelete /dev/sdb1 –inode 2     # 读取目标磁盘sdb1上面的文件状态,返回内容如下示例:
# 注:一般一个分区挂载到一个目录下时,这个”根”目录的inode值一般为2,可以通过“ls -id”命令来查看某个文件或者目录的inode值(例如:ls -id /)。我们为了查看根目录所有文件,所以查看分区inode为2的这个部分。

NOTICE: Extended attributes are not restored.
Loading filesystem metadata ... 800 groups loaded.
Group: 0
Contents of inode 2:
########## 省略一大坨 ############
File name                                       | Inode number | Deleted status
.                                                 2
..                                                2
home                                              2310145
logs                                              5718017
backup                                            2236417        Deleted
database                                          4014081        Deleted
########## 继续省略一大坨 ############

这里已经看到了要恢复的目录。这时只需插上另外一块硬盘来恢复目标文件即可。

如果能正常进人系统,还能用网卡(非Recovery模式启动),则可在一台机器上部署NFS Server,然后在本机上挂载NFS设备来保存恢复的文件,如下示例;

# 服务端(找着了台centos机器做NFS服务端)
echo '/data/tmp IP(rw,sync,no_root_squash)' >> /etc/exports
/etc/init.d/rpcbind start
/etc/init.d/nfs start

# 客户端(挂载)
mount -o nolock -t nfs IP:/data/tmp /mnt

开始恢复(假设我们的数据存放在database下,那么只需要恢复这个目录即可)

# 恢复目录先做个软链接到刚刚挂载的NFS磁盘上。
mkdir -p /mnt/RECOVERED_FILES && ln -s /mnt/RECOVERED_FILES /usr/local/extundelete/bin/

# 执行恢复 /usr/local/extundelete/bin/extundelete /dev/sdb1 --restore-directory database
NOTICE: Extended attributes are not restored. 
Loading filesystem metadata ... 800 groups loaded. 
Loading journal descriptors ... 26244 descriptors loaded. 
Searching for recoverable inodes in directory database ... 331 recoverable inodes found. 
Looking through the directory structure for deleted files ... 
# 恢复完后,便可以在对应的目录里看到恢复出来的文件。

extundelete详解

--after-dtime <dtime>          # 只恢复指定时间(时间戳)之后被删除的数据
--before-dtime <dtime>         # 只恢复指定时间(时间戳)之前被删除的数据
--restore-inode <inode>        # 恢复一个或多个指定inode号的文件,该恢复的文件名为<filename>.<inode>
--restore-file <file>          # 恢复指定的文件(被删除的),文件名还是原来的
--restore-files <file>         # 恢复指定的文件(真实存在的)中的内容
--restore-directory <dir>      # 恢复指定的目录
--restore-all                  # 恢复某分区里所有被删除的数据,文件名还是原来的

 

恢复MySQL数据库

1、如果找回的数据目录/文件完整,那么只需要修改一下my.conf,将datadir指向刚刚还原出来的数据库目录,然后执行/usr/local/services/mysql/bin/mysqld_safe & | systemctl start mysqld启动MySQL即可。但不出所料,启动挂掉,报错日志内容如下:

InnoDB: Reading tablespace information from the .ibd files...
InnoDB: Restoring possible half-written data pages from the doublewrite
InnoDB: buffer...
InnoDB: Doing recovery: scanned up to log sequence number 9120034833
170322 13:12:51 InnoDB: Starting an apply batch of log records to the database...
InnoDB: Progress in percents: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 160822 13:12:51 [ERROR] mysqld got signal 11 ;
This could be because you hit a bug. It is also possible that this binary
or one of the libraries it was linked against is corrupt, improperly built,
or misconfigured. This error can also be caused by malfunctioning hardware.
To report this bug, see http://kb.askmonty.org/en/reporting-bugs
We will try our best to scrape up some info that will hopefully help
diagnose the problem, but since we have already crashed, 
something is definitely wrong and this may fail.

2、这里主要关注 mysqld got signal 11 ,从日志内容分析来看,应该是上一步恢复出来的文件是不完整的,日志文件损坏,无法正常恢复。

3、因为日志已经损坏,这里采用非常规手段,首先修改innodb_force_recovery参数,使mysqld跳过恢复步骤,将mysqld 启动,将数据导出来然后重建数据库。

innodb_force_recovery可以设置为1-6,大的数字包含前面所有数字的影响:

1    # SRV_FORCE_IGNORE_CORRUPT,即忽略检查到的corrupt页。
2    # SRV_FORCE_NO_BACKGROUND,即阻止主线程的运行,如主线程需要执行full purge操作,会导致crash。
3    # SRV_FORCE_NO_TRX_UNDO,即不执行事务回滚操作。
4    # SRV_FORCE_NO_IBUF_MERGE,即不执行插入缓冲的合并操作。
5    # SRV_FORCE_NO_UNDO_LOG_SCAN,即不查看重做日志,InnoDB存储引擎会将未提交的事务视为已提交。
6    # SRV_FORCE_NO_LOG_REDO,不执行前滚的操作。

注意:

(1)、当设置参数值大于0后,可以对表进行select,create,drop操作,但insert,update或者delete这类操作是不允许的。

(2)、当innodb_purge_threads 和 innodb_force_recovery一起设置会出现一种loop现象:

170322 14:20:42 InnoDB: Waiting for the background threads to start
170322 14:20:43 InnoDB: Waiting for the background threads to start
170322 14:20:44 InnoDB: Waiting for the background threads to start
170322 14:20:45 InnoDB: Waiting for the background threads to start

4、在my.cnf中修改以下两个参数

innodb_force_recovery=1
innodb_purge_thread=0

5、重启MySQL,因为mysql的root密码文件也没了,这里我们用安全模式启动:

/usr/local/mysql/bin/mysqld_safe --skip-grant-tables &

6、数据库启动了。这时候正常来说应该可以松一口气,可以用mysqldump来个逻辑备份了,但在执行备份操作时又报错:

test -d /mnt/backup/ || mkdir -p /mnt/backup/
mysqldump database > /mnt/backup/database.sql
mysqldump: Couldn't execute 'show create table xxxxxxx': File './database/xxxxxxx.MYD' not found (Errcode: 2) (29)

7、按上面的报错到数据目录看了下,傻眼了,库里有MyISAM引擎的表。而且还是MYD丢失了。只剩下了.frm和.MYI。搜了一圈,没找着合适的方法。最后对比了下原备份的数据。报的这几个表里面都是没有数据或是一些不重要的数据。于是可以放心的干掉这几个有问题的表。只恢复重要的数据。

8、安全起见,还是用了一下mysqlcheck来修复了一下数据库。mysqlcheck database 运气较好,这几个丢的MYD的表都是些没数据或是不重要的表。去掉后继续 mysqldump 数据。还来:

mysqldump database > /mnt/backup/database.sql  
mysqldump: Got error: 1146: Table '' doesn't exist when using LOCK TABLES

9、这回的是innoDB的表有问题,按上面报错的话,一般是以下几个原因:

  • 已经删除且重新创建了 InnoDB 数据文件,但是忘了从数据库目录删除相应的 InnoDB 表的 .frm 文件,或者您已经移动 .frm 文件到其它的数据库。检查 show tables
  • mysql 数据目录中表文件的权限和所有权不正确
  • 表文件已被损坏
  • 表文件是用大写形式创建的

解决方法

    • 检查 show tables,如果未列举表文件,请将 .frm 文件移出数据库目录。
    • mysql 数据目录中表文件的权限和所有权不正确,理想的所有权限应为 mysql 用户和权限 660。
    • 修复表文件
    • 设置 lower_case_table_names

10、看了其他几条,都不是。只能是恢复数据的时候没有恢复完。丢失了.idb文件。

11、这次的运气还是比较好,丢失的.idb表也是一直不重要的表。直接把.frm文件移了出去。

12、重新mysqldump试试

mysqldump database > /mnt/backup/database.sql
echo $?
0

13、成功备份

总结

  1. 即使是还没上线的项目,备份也很有必要。
  2. 生产环境,用非 root 用户操作,能大大减少误操作的概率。
  3. 恢复过程中,有会丢失个别表数据的情况,虽然说这些数据对于我们这次的数据来说不太重要。但如果发生在真实环境中,刚好丢失了重要的数据,那真是欲哭无泪了。
  4. 线上环境中,如果需要快速恢复业务,那么中间遇到的几个表异常,应该先确认是否为重要数据,如不重要直接跳过先恢复有用的数据。后面的再想办法恢复。

Leave A Comment

作者: Lawrence Lang

一位资深Linux云计算、云原生、大数据、高可用集群运维架构工程师。