数据库丢失但没备份过?救星:二进制日志
前言
笔者之前的Haven项目,宝塔上的MySql经常自己关闭,重启MySql后数据库丢失。奈何我有备份,根本不怕,丢失我就重建。
由于这个项目搁置了很久,它的服务器已经被我释放了。然而现在需要申请软著,于是我将Haven项目与我现阶段参与的SoulSprout项目共用同一个服务器(其实前者也就用了个数据库)。(SoulSprout体量比Haven大但从未出现过MySql宕机的问题,最近一个月我也就没有备份)
次日,数据库打不开,发现MySql又自己关闭了!(这Haven项目的数据库是不是有病毒啊,我真服了,截至现在,我仍不能断定MySql宕机的具体原因)。问题是SoulSprout这个项目的数据库没备份。咋恢复呢?
解决方法:通过二进制日志的信息恢复
Moudle 1 : 宝塔MySql宕机及表丢失原因分析
根本原因分析
1. 为什么数据库会自己关闭?
- 可能的原因包括:服务器资源不足(内存、磁盘空间)、MySQL配置问题、系统崩溃、MySQL bug、强制重启服务器等。
- 从二进制日志中,看到了很多强制关闭线程的警告,例如:
[Warning] [MY-010909] [Server] /www/server/mysql/bin/mysqld: Forcing close of thread 75044 user: 'news'.
这通常是因为在关闭MySQL时,有连接没有正常关闭,MySQL强制关闭它们。
2. 为什么重启之后表会丢失?
· 从日志中发现了一条 DROP database \
soulsprout`` 语句。这意味着在某个时间点,执行了删除数据库的操作。
# 在二进制日志中发现的危险操作
DROP database `soulsprout`
可能的发生场景:
- 应用程序或脚本错误执行了删除数据库的操作
- 部署流程中的自动化脚本误操作
- 数据库维护过程中的误操作
- 恶意操作或安全漏洞被利用
但是,从二进制日志中我看到了多次创建和删除数据库的操作,例如:
DROP database soulsprout
CREATE USER ...
DROP USER ...
等等
这似乎不是一次意外,而可能是某种自动化脚本或部署流程中的操作。
另外,我们注意到在二进制日志中,在删除数据库之后,又有创建表和数据插入的操作。所以,实际上在删除数据库之后,数据库又被重新创建并使用了,直到最后一次删除操作后没有重新创建,然后我们遇到了问题。
3. 重启后表消失的机制
- MySQL正常关闭时会将内存中的数据刷写到磁盘
- 但异常关闭可能导致:
- 数据文件损坏
- 事务未完全提交
- 表空间信息丢失
- 重启时MySQL尝试恢复,但遇到
DROP DATABASE
指令执行
Moudle 2 : 完整的故障恢复流程梳理
第一阶段:问题诊断
步骤1:确认问题范围
# 检查数据库状态
/etc/init.d/mysqld status
# 检查数据目录
ls -la /www/server/data/soulsprout
发现:数据库目录存在但为空
步骤2:检查MySQL错误日志
tail -100 /www/server/data/iZbp1bhyfrb73bpfb49a4rZ.err
发现:大量强制关闭线程的警告,但无表结构相关错误
步骤3:检查二进制日志状态
# 确认二进制日志文件存在
ls -la /www/server/data/mysql-bin.00000*
第二阶段:恢复策略制定
关键发现
- 二进制日志文件完整存在(mysql-bin.000004, mysql-bin.000005)
- 二进制日志包含完整的数据操作记录
- 需要确定是否包含表结构定义
验证二进制日志内容
./mysqlbinlog /www/server/data/mysql-bin.000004 | grep -i "CREATE TABLE" | head -10
重要发现:二进制日志中包含完整的表结构定义!到这算是放心了,本来以为只有DML数据的操作信息,没想到也有DDL数据定义的信息
现在既能找到表结构信息,又能找到表数据信息,下面就简单了
第三阶段:执行恢复
步骤1:定位恢复起点
./mysqlbinlog --base64-output=decode-rows -v /www/server/data/mysql-bin.000004 | \
grep -A 10 -B 5 "CREATE TABLE \`user\`" | head -20
找到关键位置:# at 6966
这是从binlog中提取出自 CREATE TABLE
语句之后的所有操作,跳过之前导致数据丢失的 DROP
语句。
步骤2:执行精准恢复
# 从第一个CREATE TABLE语句开始恢复
./mysqlbinlog --start-position=6966 --database=soulsprout \
/www/server/data/mysql-bin.000004 /www/server/data/mysql-bin.000005 | \
mysql -u root -p soulsprout
步骤3:处理恢复中的问题
- 我遇到的问题:
Can't create database 'soulsprout'; database exists
- 原因:我手动创建了数据库,所以出现了此问题
- 解决方案:跳过CREATE DATABASE语句
步骤4:最终成功方案
# 清理环境
mysql -u root -p -e "DROP DATABASE soulsprout;"
mysql -u root -p -e "CREATE DATABASE soulsprout;"# 执行恢复
./mysqlbinlog --start-position=6966 --database=soulsprout \
/www/server/data/mysql-bin.000004 /www/server/data/mysql-bin.000005 | \
mysql -u root -p soulsprout
第四阶段:验证恢复结果
- 来到navcat查看数据库发现表以及数据全部恢复
- 进入网站,数据正常,业务正常
梳理一下整个流程
- 发现问题:MySQL重启后,表全部消失。
- 检查数据目录:发现数据库目录(soulsprout)存在,但里面没有表文件(.frm, .ibd等)。
- 检查二进制日志:发现二进制日志(mysql-bin.000004和mysql-bin.000005)还在,并且其中包含了完整的数据库操作(包括创建数据库、创建表、插入、更新、删除等)。
- 尝试从二进制日志恢复:
a. 首先,我们尝试直接使用二进制日志恢复,但遇到了错误,因为二进制日志中包含删除数据库的语句,而数据库已经存在。
b. 然后,我们检查了二进制日志中是否包含表结构,发现确实有CREATE TABLE语句。
c. 我们通过找到第一个CREATE TABLE语句的位置(position 6966),然后从这个位置开始恢复,跳过了之前的删除数据库操作。
d. 在恢复过程中,我们遇到了表已存在的错误,因此我们选择了删除现有数据库,然后重新执行恢复。
e. 最终,我们成功恢复了数据库的表结构和数据。
Moudle 3 : 预防措施建议
1. 备份!!!
建议的备份方案:使用宝塔面板的自动备份功能,每日全量备份 + 每小时增量备份
2. 数据库配置优化
确保二进制日志开启,这也是我能在没有备份的情况下重新恢复数据库的根本原因
# 在my.cnf中增加安全配置
[mysqld]
# 确保二进制日志开启
log-bin=mysql-bin
# 设置二进制日志过期时间(建议7-30天)
expire_logs_days=7
# 启用慢查询日志
slow_query_log=1
# 设置安全参数
local_infile=0
3. 权限管理加强
-- 限制DROP权限
REVOKE DROP ON *.* FROM 'application_user'@'%';
-- 创建专用备份用户
CREATE USER 'backup_user'@'localhost' IDENTIFIED BY 'strong_password';
GRANT SELECT, RELOAD, LOCK TABLES ON *.* TO 'backup_user'@'localhost';