本文介绍的闪回操作只适用于小范围“精准手术”,大规模“重建”请使用备份恢复。
一、背景说明
数据库版本:MySQL 5.7.44
binlog 配置:binlog_format = ROW,binlog_row_image = FULL
问题描述:jc-iot-connection_prod 库中 l_cmp_internal_order 表误删了一条记录(时间大概是下午 4点到 4点半)
恢复目标:利用 binlog 闪回技术,将误删数据恢复至删除前的完整状态
二、恢复实操
1、定位 binlog 日志文件
方法一:通过 strings 快速检索时间戳
strings /www/server/data/mysql-bin.034683 | grep "26-08-18" | head -20方法二:根据文件修改时间推断(推荐)
ll -h /www/server/data/mysql-bin.0346*
# 输出示例:
#-rw-r----- 1 mysql mysql 1.1G Aug 18 15:45 mysql-bin.034681
#-rw-r----- 1 mysql mysql 1.1G Aug 18 16:14 mysql-bin.034682
#-rw-r----- 1 mysql mysql 1.1G Aug 18 16:45 mysql-bin.034683说明:文件的修改时间约等于 binlog 记录的最后写入时间。通过对比可知,mysql-bin.034682 覆盖了 15:45 ~ 16:14 的日志段,与误删时间(约 16:12)最为接近。
如果不能确定误删时间,可通过下述的【定位提取 SQL 操作】确定。
2、导出目标时间段日志
/www/server/mysql/bin/mysqlbinlog \
--start-datetime="2026-08-18 16:00:00" \
--stop-datetime="2026-08-18 16:15:00" \
/www/server/data/mysql-bin.034682 \
-v --base64-output=decode-rows > gavin_binlog.txt参数说明:
--start-datetime:包含 ≥ 该时间点的事件
--stop-datetime:包含 < 该时间点的事件(不包含等于)
时间格式的精度是秒级。16:13 会自动补齐为 16:13:00
-v:显示行数据详情
--base64-output=decode-rows:将 binlog 中的行事件解码为可读的 SQL 注释格式
(进阶)同时扫描两个文件,确保覆盖 binlog 切换边界:
/www/server/mysql/bin/mysqlbinlog \
--start-datetime="2026-08-18 16:12:00" \
--stop-datetime="2026-08-18 16:15:00" \
/www/server/data/mysql-bin.034682 \
/www/server/data/mysql-bin.034683 \
-v --base64-output=decode-rows \
> gavin_binlog_merged.txt时间窗口覆盖 16:12:00 ~ 16:14:59.999
两个文件按顺序自动合并
不会漏数据,不会重复
3、定位并提取 DELETE 操作
(1)搜索 DELETE 语句位置
# 搜索 DELETE FROM 语句(包含前后几行上下文)
grep -n -i "DELETE FROM" gavin_binlog.txt -B 5 -A 20
# 搜索包含你的表名和 DELETE 的内容
grep -n -i "你的表名" gavin_binlog.txt | grep -i "DELETE" -B 5 -A 20
# 扩展
grep -n "### DELETE FROM.*你的表名" gavin_binlog.txt
grep -n "### UPDATE.*你的表名" gavin_binlog.txt
grep -n "### INSERT INTO.*你的表名" gavin_binlog.txt-i:--ignore-case
-n:--line-number
-B 5 -A 20:匹配行及前 5 行、后 20 行
实战操作:
grep -n -i "l_cmp_internal_order" gavin_binlog.txt | grep -i "DELETE" -B 5 -A 20
# 输出示例:
46016803:#260818 16:12:38 server id 1 end_log_pos 990837208 CRC32 0xf3001a30 Table_map: `jc-iot-connection_prod`.`l_cmp_internal_order` mapped to number 4379
46016806:### DELETE FROM `jc-iot-connection_prod`.`l_cmp_internal_order`关键信息:DELETE 语句位于 第 46016806 行。
(2)提取 DELETE 的完整行数据
本表共有 120+ 个字段,因此需提取足够多的行以覆盖所有 @N 字段:
# 从 DELETE 行开始,向后提取 200 行(46016806 + 200 = 46017006)
sed -n '46016806,46017006p' gavin_binlog.txt输出内容格式如下:
### DELETE FROM `db`.`table`
### WHERE
### @1=9285
### @2=NULL
### @3=20260818
### @4='2026-08-18 10:21:47'
### ...
### @86='2026-08-18 10:21:47' -- creation_datetime
### @87='2026-08-18 10:21:57' -- last_update_datetime
### ...
### @122=NULL
# at 990839029
# 可见,共有 122 个字段,理论最小行数是 124 行注意:@1 ~ @N 按顺序对应表字段(从左到右),需结合表结构进行映射。
(3)获取事务点位(可选)
方式一(less 分页查看):
less gavin_binlog.txt在 less 中按 / 输入 # at 990839029(或直接搜索 DELETE 行号附近),定位到该事务区域,可看到完整的事务日志:
BEGIN
/*!*/;
# at 990836788
#260818 16:12:38 server id 1 end_log_pos 990837208 CRC32 0xf3001a30 Table_map: `jc-iot-connection_prod`.`l_cmp_internal_order` mapped to numb
er 4379
# at 990837208
#260818 16:12:38 server id 1 end_log_pos 990839029 CRC32 0x79d6d762 Delete_rows: table id 4379 flags: STMT_END_F
### DELETE FROM `jc-iot-connection_prod`.`l_cmp_internal_order`
### WHERE
### @1=9285
...
### @122=NULL
# at 990839029
#260818 16:12:38 server id 1 end_log_pos 990839060 CRC32 0x8d99f40b Xid = 59813553884
COMMIT/*!*/;
# at 990839060点位解析:
990836788:Table_map 映射表结构,准备操作 l_cmp_internal_order
990837208:Delete_rows 实际执行 DELETE FROM ... WHERE @1=9285 ...
990839029:Xid COMMIT,事务提交确认
990839060:GTID / 下一个事件 下一个事务的开始,当前事务结束边界
综上可得:
起始位点(--start-position):BEGIN 后面的 # at N,即 990836788
结束位点(--stop-position):COMMIT 后面的 # at N,即 990839060
方式二(sed + grep 快速提取):
# sed 前后各加几行,可简单判断的得到点位
sed -n '3209350,3209600p' gavin_binlog.txt | grep -E "BEGIN|COMMIT|# at"4、生成恢复 INSERT 语句
(1)获取表结构
SHOW CREATE TABLE `jc-iot-connection_prod`.`l_cmp_internal_order`;(2)映射字段并拼接 INSERT
将 @N 按表字段顺序一一对应,重点关注:
bit 类型:b'0' → 0,b'1' → 1
字符串类型:保持引号包裹
NULL 值:直接写 NULL
推荐:可将表结构和 DELETE 数据发给 AI 辅助生成 INSERT 语句,效率更高且不易出错。
(3)安全执行恢复
BEGIN;
INSERT INTO `jc-iot-connection_prod`.`l_cmp_internal_order` (...) VALUES (...);
-- 验证
SELECT * FROM `jc-iot-connection_prod`.`l_cmp_internal_order` WHERE `uid` = 9285;
COMMIT; -- 确认无误后提交
-- ROLLBACK; -- 如有问题则回滚三、总结
通过 mysqlbinlog 解析 ROW 格式的 binlog,并结合 grep / sed 精准定位 DELETE 操作,再反向生成 INSERT 语句,即可实现误删数据的精确恢复。此方法无需额外工具,适用于所有 MySQL 5.7+ 环境,掌握后可快速应对各类误操作事故。
核心逻辑:
定位 → 提取 → 映射 → 回写,四步完成一次标准闪回。
附录:备选工具 binlog2sql 与 my2sql
当误删数据涉及批量操作(如无 WHERE 条件的 DELETE),用 mysqlbinlog 手动整理成百上千条 INSERT 语句将非常低效。此时,闪回工具是更优选择。它们能自动解析 binlog,直接生成反向 SQL,大幅提高恢复效率。
使用前提:无论选择哪个工具,均需满足以下条件:
binlog_format = ROW
binlog_row_image = FULL只能回滚 DML(INSERT / UPDATE / DELETE),无法闪回 DDL(如 DROP TABLE / TRUNCATE)
1、binlog2sql
binlog2sql是一款经典的 Python 工具,由大众点评开源。它安装简单,命令直观,适合处理中小规模数据的快速回滚。
适用场景:中小规模数据恢复、Python 环境熟悉的应急场景。
(1)安装与权限准备
# 下载
git clone https://github.com/danfengcao/binlog2sql.git
cd binlog2sql
# 安装依赖
pip install -r requirements.txtMySQL 账号需具备以下权限:
-- IDENTIFIED BY:
-- 如果用户不存在,会自动创建该用户并设置密码
-- 如果用户已存在,会更新该用户的密码
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'rec_user'@'%' IDENTIFIED BY 'your_password';
FLUSH PRIVILEGES;
-- 查看 rec_user 的权限
SHOW GRANTS FOR 'rec_user'@'%';注意:
binlog2sql项目最后一次更新在 2018 年,对 MySQL 8.0 及以上版本可能存在兼容性问题,建议在 5.6/5.7 环境中使用。
(2)使用示例
python binlog2sql.py -h127.0.0.1 -P3306 -urec_user -p'your_password' \
-d jc-iot-connection_prod \
-t l_cmp_internal_order \
--start-file='mysql-bin.034682' \
--start-datetime='2026-08-18 16:12:00' \
--stop-datetime='2026-08-18 16:13:00' \
-B > rollback.sql执行后,
rollback.sql中即包含自动生成的反向 SQL,审核后即可执行恢复。
参数解析:
--start-file:起始解析文件(必须指定)
--stop-file / --end-file:末尾解析文件,默认为 --start-file 同一个文件
--start-position / --start-pos:起始解析位置,默认为文件开头
--stop-position / --end-pos:末尾解析位置,默认为文件最末位置
-B:反向 SQL 模式(DELETE → INSERT,UPDATE → 反向 UPDATE)
2、my2sql
my2sql 采用 Go 语言编写,提供静态编译的二进制文件,无需依赖 Python 环境。它对大文件解析速度快、内存占用低,且支持 MySQL 8.0,是当前生产环境批量恢复的更优选择。
适用场景:大文件(GB 级以上)、批量数据恢复、MySQL 8.0 环境、需要 DML 统计等高级功能的场景。
(1)安装与权限准备
# 下载二进制文件(以 CentOS 7 / Linux amd64 为例,单个可执行文件,8M 左右)
# 下载极慢,建议开代理下载,下载完成再上传到 CentOS 7
wget https://raw.githubusercontent.com/liuhr/my2sql/master/releases/centOS_release_7.x/my2sql
# 赋予执行权限
chmod +x my2sql
# 可选
mv my2sql /usr/local/bin/MySQL 账号权限要求与 binlog2sql 相同:
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'rec_user'@'%';(2)使用示例
方式一:按位点生成回滚 SQL(推荐,最精准)
先用 mysqlbinlog 定位误操作事务的起止位点,再交由 my2sql 精准闪回:
# 1. 确保输出目录存在
mkdir -p ./recover_01
# 2. 生成回滚 sql
./my2sql -user rec_user -password 'your_password' -host 127.0.0.1 \
-work-type rollback \
-start-file mysql-bin.034682 \
-start-pos 990836788 -stop-pos 990839060\
-databases jc-iot-connection_prod \
-tables l_cmp_internal_order \
-output-dir ./recover_01执行后,回滚 SQL 生成在 ./recover_01/rollback.sql 中。
方式二:按时间范围生成回滚 SQL
mkdir -p ./recover_by_time
./my2sql -user root -password 'your_password' -host 127.0.0.1 \
-work-type rollback \
-start-file mysql-bin.034682 \
-start-datetime "2026-08-18 16:12:00" \
-stop-datetime "2026-08-18 16:13:00" \
-databases jc-iot-connection_prod \
-tables l_cmp_internal_order \
-sql delete \
-output-dir ./recover_by_time参数解析:
-sql delete:只解析 DELETE 操作
-add-extraInfo:添加位点、时间等注释(犯罪现场)。
方式三:跨文件解析
my2sql 支持同时传入多个 binlog 文件,自动处理跨文件边界:
mkdir -p ./recover_by_time
./my2sql -user rec_user -password 'your_password' -host 127.0.0.1 \
-work-type rollback \
-start-file mysql-bin.034682,mysql-bin.034683 \
-start-datetime "2026-08-18 16:12:00" \
-stop-datetime "2026-08-18 16:15:00" \
-databases jc-iot-connection_prod \
-tables l_cmp_internal_order \
-output-dir ./recover_cross_file