MySQL的--database参数不可靠,因它仅过滤默认库匹配的语句,无法识别INSERT INTO other_db.table等显式跨库操作,且在GTID模式下可能被忽略;可靠做法是分四步:定位位置→导出裸日志→正则文本过滤→安全重放,并注意GTID保留与事务边界校验。

MySQL只恢复某个库的Binlog,为什么mysqlbinlog --database不靠谱
因为--database参数只过滤“当前默认库”为指定值的语句,不识别INSERT INTO other_db.table这类显式带库名的操作。实际生产中,跨库写入、存储过程、触发器都可能绕过这个过滤,导致漏恢复或误恢复。
真正可控的方式是:先用mysqlbinlog把目标时间段的原始日志导出,再用正则或工具二次清洗——只保留涉及目标库的USE、CREATE、DROP、INSERT、UPDATE、DELETE等语句,并确保所有表引用都带库前缀(如`mydb`.`t1`)。
- 导出时加
--base64-output=DECODE-ROWS --verbose便于后续人工核对 - 避免用
--database直接重放,它在GTID模式下甚至会被忽略 - 如果用了
replicate_do_db,注意它只影响复制线程,对本地mysql命令重放无效
用mysqlbinlog提取并重放单库SQL的实操步骤
核心不是靠参数一步到位,而是分三步:定位位置 → 提取裸日志 → 文本过滤 → 安全重放。
例如要恢复payment库从mysql-bin.000123中第123456位到末尾的内容:
- 先运行
mysqlbinlog --start-position=123456 mysql-bin.000123 > raw.sql - 再用
grep -E "^USE `payment`|`payment`\." raw.sql > payment-only.sql粗筛(注意反引号和点号转义) - 手动检查前几条,确认没有
USE `otherdb`后紧跟INSERT INTO t1这种隐式跨库操作 - 重放前务必加
SET sql_log_bin = 0;,否则会再次写入binlog,引发循环或冲突
GTID环境下按库恢复更麻烦,因为--exclude-gtids不能按库过滤
GTID本身不记录库名,只记录事务全局ID。你无法用GTID范围直接表达“只取payment库的事务”。必须退回到基于position或datetime的切片方式,再配合文本过滤。
如果你启用了gtid_mode=ON,还要注意:mysqlbinlog输出里每条事务开头都有SET @@SESSION.GTID_NEXT='xxx',这些语句不能删,否则重放会报The GTID specified has already been used错误。
- 不要用
sed '/GTID_NEXT/d'删GTID行 - 若需跳过某些事务,用
--exclude-gtids配合已知GTID集合,但得先从SHOW MASTER STATUS或mysqlbinlog --base64-output=DECODE-ROWS里人工提取目标库相关事务的GTID - 更稳妥的做法:在从库上临时配置
replicate_ignore_db以外的库,然后用START SLAVE UNTIL停在目标位置,再导出relay log
重放后校验库级一致性,别只看SELECT COUNT(*)
数行数几乎没用——空表、被删又重建、主键重复插入都会让COUNT看起来正常,但数据已错。重点核验三类东西:
-
SHOW CREATE TABLE结果是否和备份时刻一致(尤其ENGINE、CHARSET、AUTO_INCREMENT值) - 用
mysqldump --no-data payment比对表结构,用mysqldump --skip-triggers --where="1=1" payment抽样导出几万行做MD5比对 - 查
information_schema.TABLES里的UPDATE_TIME,确认重放后没有意外更新(比如触发器偷偷改了其他表)
最易被忽略的是时间点精度:binlog position只是近似边界,一个事务可能横跨多个position。恢复时如果只截到某条COMMIT中间,整个事务就残缺了——所以永远优先用--stop-datetime配合人工确认事务边界,而不是盲目信position。










