MySQL Binlog(主从同步/数据恢复)及 mysqldump,这两个东西放在一起聊,是因为它们才是数据库日常运维里真正能救命的两条腿。很多同学数据库一挂,第一反应是看有没有做备份,做了备份之后又发现只能恢复到备份那一刻,中间几个小时的业务数据全没了。问题出在哪里?就是只备份了全量数据,但没有把增量变更记下来。Binlog负责记录每一次数据变更,mysqldump负责给你一个可恢复的基线,两者配合起来,才能实现最短时间窗口的恢复。这篇文章我把原理和实操放在一起讲,适合后端开发、运维、DBA,也适合自己搭数据库的个人开发者。不聊虚的,直接讲清楚怎么配、怎么用、什么场景下怎么搞。
1. 先把概念理清楚:binlog 到底记了什么
1.1 binlog 是 MySQL 的“飞行记录仪”
binlog(Binary Log)是 MySQL Server 层维护的二进制日志文件,记录所有能改变数据库内容的操作:INSERT、UPDATE、DELETE、CREATE TABLE、ALTER TABLE 等等,SELECT 查询不会记录。它记录的不是数据在磁盘上怎么摆的物理格式,而是这些变更的逻辑描述,所以在主从同步和数据恢复场景里,它几乎是唯一的选择。注意这里要区分两个概念:InnoDB 存储引擎有自己的 redo log,那是物理日志,记录数据页的修改,且是循环覆盖的,主要用来保证崩溃恢复;binlog 是逻辑日志,追加写入,记录的是“哪个事务做了什么事”。
我在实际操作中经常把 binlog 比作飞行记录仪:平时你可能几个月不会去看它一眼,但它一直在后台把每一次起飞降落的参数记下来。等哪天出了事,你把记录调出来一帧一帧回放,就能知道问题出在哪个时间点、哪个事务上。没有这个记录,数据丢了就是真的丢了。
选择 binlog 需要考虑的第一点:既然要使用它,就必须确认已经开启。很多开发同学的本地库默认没开,生产库也可能因为历史原因忘记开,等你需要恢复的时候才发现没有日志可用,那才是最绝望的。检查方法很简单:
SHOW VARIABLES LIKE 'log_bin';如果返回的是 OFF,那么接下来的主从同步和时间点恢复都无从谈起。
1.2 STATEMENT、ROW、MIXED 三种格式怎么选
binlog 有三种记录格式:STATEMENT、ROW、MIXED。这条选择非常关键,直接影响后续的运维工作量和数据一致性。
STATEMENT 格式记录的是 SQL 原文。好处是日志体积小,一个 UPDATE 影响一万行,日志里就一条 UPDATE 语句。坏处是它依赖执行环境的确定性,遇到 NOW()、UUID()、RAND() 这类非确定性函数,在主库执行和从库重放时结果可能对不上。比如你插入了一条记录,写入的 create_time 用了 NOW(),主库执行时是上午 10 点整,从库因为 SQL 线程延迟重放时已经 10 点过 5 分,重放出来的 create_time 也跟着变了,主从数据就出现不一致。这种问题非常隐蔽,线上查半天都未必查得出来。
ROW 格式记录的是每一行数据变更前后的完整映像,DELETE 会记录被删除行的完整内容,UPDATE 会记录更新前后的值。它最大的好处是重放结果与主库严格一致,不依赖上下文,不管什么时间点重放,得到的数据都一样。缺点也很明显:日志量大。我遇到过一条不带 WHERE 的 UPDATE,全表 50 万行全被更新,binlog 瞬间写了几个 G。但是拿一致性换磁盘空间,这笔账怎么算都划算。
MIXED 格式是 MySQL 自动判断,一般情况下用 STATEMENT,遇到可能不安全的函数自动转成 ROW。看起来两边的好处都占了,但我个人的生产环境推荐始终用 ROW。因为 MIXED 的“自动判断”有时会误判,比如带有 LIMIT 的 UPDATE、不带主键的 DELETE,都可能出现预料不到的行为。排查问题的时候,你没法确定哪些变更被转成了 ROW、哪些还是语句级别,分析起来很痛苦。ROW 格式配合解码参数,能精确看到每一行数据变成什么样,对排查线上问题非常有价值。
确认当前格式:
SHOW VARIABLES LIKE 'binlog_format';配置文件里这样设:
binlog_format = ROW1.3 binlog 关键参数配置清单
真正的生产环境,binlog 相关参数我一般按下面这套来配,每一项都有它的道理:
# 开启 binlog log_bin = ON # 服务器唯一标识,主从环境里不能重复 server_id = 100 # 记录格式,推荐 ROW binlog_format = ROW # 单个 binlog 文件大小,超过会触发自动滚动 max_binlog_size = 512M # 自动清理保留 7 天,也可以按秒设置 binlog_expire_logs_seconds expire_logs_days = 7 # 每次事务提交都刷盘,保证 binlog 不丢 sync_binlog = 1 # ROW 格式下只记录被修改的列,减少日志量 binlog_row_image = MINIMALsync_binlog = 1 是很多同学容易忽略的。默认情况下 binlog 写入是依赖系统缓存刷新的,如果数据库所在机器断电或者进程异常退出,缓存里最近几个事务的 binlog 可能还没落盘,恢复时就会丢掉最后一段日志。把它设为 1,表示每个事务提交时都强制把 binlog 刷到磁盘,牺牲一点性能,换来的是一致性和可恢复性。主从同步场景下这个参数不设,主库宕机后很容易出现从库数据比主库多或者少的情况。
expire_logs_days 也要根据恢复需求来定。如果数据库变更频繁,保留 7 天的 binlog 意味着你最多可以恢复到 7 天内的任意时间点;如果想恢复到更早,就得有更早的全量备份配合,或者把 binlog 归档到其他存储。实践里我通常把保留时间设成备份周期的两倍以上,比如每天做一次全量备份,binlog 至少保留 3 天,这样就算备份文件出了问题也有缓冲时间。
2. 主从同步:怎么把一个库变成两个库
2.1 主从同步的核心流程
先讲清楚主从同步到底是怎么工作的。很多人配置完主从就放着不管,出了问题完全不知道从哪查,就是因为不了解背后的流程。
主从复制涉及三个角色:主库的 dump 线程、从库的 IO 线程、从库的 SQL 线程。流程是这样的:
- 从库执行 CHANGE MASTER TO 指定主库地址、binlog 文件名和偏移量,然后 START SLAVE。
- 从库的 IO 线程连接主库,告诉主库“我从哪个文件哪个位置开始要日志”。
- 主库的 dump 线程从指定位置读取 binlog,推送给从库的 IO 线程。
- 从库的 IO 线程把收到的日志写到本机的 relay log(中继日志)里。
- 从库的 SQL 线程读取 relay log,把日志里的事务逐个重放到从库数据上。
可以打个比方:主库是出版社,dump 线程是发行部,从库的 IO 线程是仓库管理员负责收货存仓,SQL 线程是拆包员,把包裹里的内容抄写进自己的笔记本。任何一个环节出问题,最终表现都是副本数据和主库不一致,但根因可能完全不同。所以排查的时候第一步永远是看 SHOW SLAVE STATUS,确认两个线程是否都在正常跑:IO 线程是 Yes、SQL 线程是 Yes,才能说明复制链路是通的。
2.2 传统位点方式配置实操
传统的主从同步配置逻辑是:从库指定“从主库的哪个 binlog 文件、哪个位置开始同步”。这个位置要在主库上查。
先保证主库配置好:
log_bin = ON server_id = 100 binlog_format = ROW创建复制专用账号,权限最小化原则:
CREATE USER 'repl'@'%' IDENTIFIED BY 'StrongPass_123'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%'; FLUSH PRIVILEGES;接下来,在把数据从主库搬到从库之前,要拿到当前 binlog 的位置。避免新数据在主库产生时位置就变了,所以你需要在数据导出的过程中保持主库不写操作。我实际用的方式是 mysqldump 配合 --master-data=2,导出备份的同事会把 binlog 位置记录在 dump 文件里,这样就不用手动锁表。如果库比较小,也可以直接在命令行:
FLUSH TABLES WITH READ LOCK; SHOW MASTER STATUS;SHOW MASTER STATUS 输出里 File 和 Position 就是要记录的位点。拿到之后解锁:
UNLOCK TABLES;然后导数据。最稳妥的做法是:
mysqldump -u root -p --single-transaction --master-data=2 --set-gtid-purged=OFF --all-databases > backup.sql这个命令的具体参数作用后面会详细说,这里先记住 master-data=2 会把主库的 binlog 位点写进备份文件头部注释里,非常实用。
把 backup.sql 导入从库后,在从库执行:
CHANGE MASTER TO MASTER_HOST='主库IP', MASTER_PORT=3306, MASTER_USER='repl', MASTER_PASSWORD='StrongPass_123', MASTER_LOG_FILE='mysql-bin.000008', MASTER_LOG_POS=154, MASTER_CONNECT_RETRY=10; START SLAVE;启动后立刻看状态:
SHOW SLAVE STATUS\G关注三个地方:Slave_IO_Running 是否为 Yes,Slave_SQL_Running 是否为 Yes,Seconds_Behind_Master 是否为 0 或很小。IO 线程是 Yes 说明能连上主库拿日志,SQL 线程是 Yes 说明重放正常,延迟值为 0 说明已经完全追上主库。
2.3 GTID 方式:更省心的新方案
传统位点方式有个痛点:从库搭建时必须手工记录文件位置,如果中间日志滚动过,或者主从角色切换了,找到正确的起点很麻烦。GTID(Global Transaction Identifier,全局事务标识)把这个痛点解决了。
GTID 的设计思想是:每个事务在主库提交时获得一个全局唯一编号,格式类似 UUID:1。因为每个事务都有唯一 ID,从库可以直接说“我已经执行到哪个 GTID 了,后续的请继续给我”,不需要关心 binlog 文件名和偏移量,主库自动从对应位置开始推。
启用 GTID 要在主从所有节点配置:
gtid_mode = ON enforce_gtid_consistency = ON从库的 CHANGE MASTER 语句就不用写 MASTER_LOG_FILE 和 MASTER_LOG_POS 了,替换成:
CHANGE MASTER TO MASTER_HOST='主库IP', MASTER_PORT=3306, MASTER_USER='repl', MASTER_PASSWORD='StrongPass_123', MASTER_AUTO_POSITION = 1; START SLAVE;MASTER_AUTO_POSITION = 1 表示用 GTID 自动定位。我个人的倾向是,新搭建的 MySQL 5.7 及以上环境,一律用 GTID,别走回头路。它带来的最大好处是主从切换或者从库重建时,你不需要再去对比 binlog 文件位置,运维效率提升非常明显。
2.4 主从延迟的几个常见诱因
主从同步配置好了,不代表就一劳永逸。我踩得最多的坑就是主从延迟,部分业务对副本的实时性有要求,延迟一上来就会引发问题。
第一个诱因是大事务。一次 DELETE 删除几十万行、一次 ALTER TABLE 修改表结构,这类大事务在主库执行很快,但到了从库 SQL 线程重放时是一个事务整体回放,期间从库的 SQL 线程只能等它执行完,别的变更全堵在后面。解决办法是把大事务拆成小批次,或者把 DDL 安排在业务低峰期。
第二个诱因是从库硬件弱于主库。从库不只是要跑 SQL 线程重放,它可能还承担着读流量,如果磁盘、CPU 跟不上,重放速度自然慢。检查方式很简单:看 SHOW SLAVE STATUS 里的 Seconds_Behind_Master,连续几分钟都在增长,大概率是硬件瓶颈或者慢查询。
第三个诱因是并行复制没有启用。MySQL 5.7 引入了基于组提交的并行复制,8.0 继续增强,如果配置里没开,从库重放就是单线程,大并发主库产生的日志,从库根本来不及消化。建议确认从库的并行复制参数是开启的。
3. 数据恢复:误删了别慌,binlog 能把你捞回来
3.1 恢复设计的整体思路
数据恢复的关键不是“会执行一条命令”,而是“有一套清晰的恢复预案”。我的思路永远是三步:先确定故障时间点,再恢复到对应状态,最后验证数据完整性。
场景最典型的:某业务库今天上午 10:30 收到一条误执行的 DELETE 不带 WHERE 条件,30 万条用户记录瞬间清光。此时你的武器是:昨天凌晨 2:00 的全量备份 + 从昨天凌晨 2:00 到 10:30 之前的所有 binlog。恢复的目标是把数据库恢复到 10:30 那一刻的状态,也就是误操作发生前的最后一刻。
千万注意的一个原则:恢复操作不要直接在线上库执行。正确的做法是拉起一个临时实例,在上面完成数据恢复,检查没问题后再切换或者把数据导回生产。直接拿 binlog 往生产库回放,万一位置没掐准,等于又产生一次灾难。
3.2 误删场景的完整恢复演练
我的实操流程是这样的,给定刚才那个误删场景。
第一步,暂停业务写入。恢复期间让应用停止或者只读,避免新数据进来干扰恢复结果。
第二步,把全量备份恢复到临时实例:
mysql -u root -p -h 127.0.0.1 --port=3307 < backup_2024-06-01.sql第三步,确定需要回放的 binlog 范围。全量备份文件头部如果带了 --master-data=2,可以查备份文件里的注释内容,确认备份对应的 binlog 文件与位置。误删发生的时间点则是 10:30。于是目标明确:从备份时刻的位置开始,回放到 10:30 前的一瞬间。
第四步,用 mysqlbinlog 把指定时间段的事务导出,再导入临时实例:
mysqlbinlog --start-datetime="2024-06-01 02:00:00" --stop-datetime="2024-06-01 10:29:59" /var/lib/mysql/mysql-bin.000009 | mysql -u root -p -h 127.0.0.1 --port=3307注意:如果你手里有多个 binlog 文件,且误删发生在后续文件里,就需要把从备份时刻到 10:29:59 之间的所有 binlog 文件按顺序排好,逐个回放。可以合并成一个命令:
mysqlbinlog --start-datetime="2024-06-01 02:00:00" --stop-datetime="2024-06-01 10:29:59" /var/lib/mysql/mysql-bin.000008 /var/lib/mysql/mysql-bin.000009 | mysql -u root -p -h 127.0.0.1 --port=3307第五步,验证临时实例里那 30 万条数据行数是否恢复,抽样核对几个关键字段。确认无误后,把恢复好的数据导回生产,或者直接把生产流量切到临时实例。
3.3 用 ROW 格式 binlog 定位误操作内容
如果我不知道误删的具体时间点怎么办?更常见的是只知道“上午数据好像不对了”,但具体几点、哪条 SQL 造成的没有头绪。这时候需要用 mysqlbinlog 解码 binlog 内容来定位。
ROW 格式下 binlog 默认输出是一堆不可读的二进制内容,得加两个参数解码:
mysqlbinlog --base64-output=decode-rows -vv /var/lib/mysql/mysql-bin.000009 | grep -A 5 "DELETE FROM `user`"--base64-output=decode-rows 会把 ROW 格式的记录还原成接近 SQL 的可读文本,-vv 是输出更详细的注释信息,包括每个列的变更前后值。这样你就能看到 binlog 里确实有针对 user 表的 DELETE 操作,以及被删除行的具体内容。
我一般在排查阶段先用解码模式把目标时间段所有日志翻一遍,确认误操作的精确位置,再用 --start-position 和 --stop-position 参数指定到具体偏移量来回放,这样比按时间回放更精确,也避免加减秒数带来的误差。
3.4 时刻注意的三个坑
- ROW 格式的 binlog 回放不需要担心函数、时间不一致的问题,这恰好是选 ROW 带来的福利。如果你还在用 STATEMENT 格式,回放时遇到时间函数会非常头疼。
- 不要试图“反向恢复”一条 DELETE。官方 mysqlbinlog 不支持把 DELETE 生成对应的 INSERT 回滚。社区有 flashback 工具能做这类反向解析,但生产环境用起来限制很多。常规且安全的思路永远是回放到误操作前那一刻。
- 长时间跨度回放时,binlog 文件可能很多很大,建议先分段导出、每段回放后做一次行数抽查,避免到最后才发现中间某段出问题。
4. mysqldump:备份这件事,最怕的就是“你以为做了”
4.1 为什么有了 binlog 还不够
binlog 解决的是增量问题,但它依赖一个基线。如果从来没有做过全量备份,手里只有 binlog,恢复的时候你要从头把整个库的所有变更都回放一遍,而且你连第一步到底该从哪个 binlog 开始都找不到。
mysqldump 是 MySQL 自带的逻辑备份工具。所谓逻辑备份,就是通过 SQL 语句来描述数据,导出的是一个包含 CREATE TABLE、INSERT 等语句的文本文件。它的优点非常直观:跨版本迁移能力很强,一台 5.7 的库导出的 SQL 基本可以在 8.0 里恢复;文件是明文,可以用 grep 查里面的内容;不会占用太多磁盘 IO。
缺点是速度慢、恢复慢,全量导出和导入时都要扫数据。所以它更适合中小型数据库,比如单库几百 G 以内的场景。再大的库,用物理备份工具比如基于 Percona 的方案更合适,直接拷贝数据文件,速度不在一个量级。但我自己的习惯是,中小业务即使有物理备份同学在跟进,mysqldump 仍然可以作为双保险保留一份。
4.2 mysqldump 核心参数逐个拆解
用 mysqldump 不是敲一句默认命令就完事。不参数化地裸调 mysqldump,很可能备份出来的文件是坏的,或者在备份过程中把线上业务锁死。下面这些参数是我实战中使用频率最高的:
--single-transaction这个参数是做 InnoDB 一致性快照备份的关键。它会在备份开始时开启一个事务,并利用 InnoDB 的 MVCC 机制读取一个一致性快照,整个过程不加表锁,业务写入照常进行。如果你的库全是 InnoDB 表,这个参数几乎是必须带的。
--master-data=2这个参数会把备份时刻对应的 binlog 文件名和 Position 写入备份文件头部注释。等将来用这份备份去搭建新的从库,或者做数据恢复时,能精确知道该从哪个 binlog 位置开始。=2 表示以注释形式写入,=1 表示直接生成一条可用于 CHANGE MASTER 的语句。我个人习惯用 =2,因为我不希望在导入备份时自动执行 CHANGE MASTER。
--set-gtid-purged=OFF如果主库启用了 GTID,备份文件里会默认带上 SET @@GLOBAL.GTID_PURGED 语句。这在某些恢复场景下会导致错误,因为你导入的备份可能和目标实例的 GTID 集合冲突。如果这个备份是给普通恢复用的,我习惯显式写 OFf 关掉它;如果是要初始化一个新的从库,则需要按需求保留或设置正确的值。
--routines --triggers --events这三个参数分别导出存储过程、触发器、事件调度器。默认情况下 mysqldump 是不导出这些的,很多同学用默认命令备份完,恢复后才发现存储过程全没了。生产环境一定要显式加上。
--quick强制逐条导出,避免一次把所有数据加载到内存。对大表来说这个参数能明显降低内存占用。
实际工作中我的典型全量备份命令长这样:
mysqldump -u backup -p'密码' \ --single-transaction \ --master-data=2 \ --set-gtid-purged=OFF \ --routines --triggers --events \ --databases mydb | gzip > /data/backup/mydb_$(date +%F).sql.gz加上管道 gzip,可以在导出同时压缩,磁盘空间省一半以上。
4.3 一个可以直接抄的备份脚本
光会敲命令还不够,备份必须形成制度,定时执行。下面这个脚本是我项目里常用的形态,放在 crontab 里每天跑:
#!/bin/bash # 全量备份脚本:每天凌晨 2 点执行 BACKUP_DIR=/data/backup/mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_USER=backup DB_PASS='xxxxxx' DB_NAME=all DATE=$(date +%F) mkdir -p "$BACKUP_DIR/$DATE" # 全量导出并压缩 mysqldump -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASS" \ --single-transaction \ --master-data=2 \ --set-gtid-purged=OFF \ --routines --triggers --events \ --all-databases | gzip > "$BACKUP_DIR/$DATE/full_backup.sql.gz" # 生成校验文件,恢复前先验文件完整性 md5sum "$BACKUP_DIR/$DATE/full_backup.sql.gz" > "$BACKUP_DIR/$DATE/full_backup.md5" # 清理 14 天前的备份,保留窗口自己控制 find "$BACKUP_DIR" -maxdepth 1 -type d -name "20*" -mtime +14 -exec rm -rf {} \;这个脚本有几个细节值得注意:备份账号用独立的 backup 账号,权限只给 SELECT、RELOAD、SHOW DATABASES、LOCK TABLES 等最小集,避免用 root 做定时任务;每次备份生成一个校验文件,恢复前先 md5 比对,防止备份文件损坏了还傻傻地去恢复;清理策略至少保留两周,宁可多占点磁盘,也别把保险给删了。
另外我强烈建议定时验证备份是否真的能恢复。嘴上说“我做了备份”不算数,定期把备份文件恢复到临时实例,确认行数、表结构没问题,这个备份才算有效。我见过不少备份任务一直在跑,但文件早就因为磁盘错误坏掉的案例,真到需要恢复的时候才发现全完了。
5. 实操中绕不开的坑,和排查建议
5.1 binlog 文件暴涨,磁盘被打满
ROW 格式最直观的副作用就是 binlog 体积变大。我遇到一次最夸张的:业务方一条不带条件的 UPDATE,全表 80 万行被更新,binlog 直接涨了 6 个 G。不是程序出 bug,就是纯粹的批量更新,但这一个事务就把磁盘占用顶满了。
排查步骤其实很简单,先看哪个 binlog 文件最大:
ls -lh /var/lib/mysql/mysql-bin.*如果确认是大事务造成的,处理思路分两层。短期:手动触发日志滚动和清理:
FLUSH BINARY LOGS; PURGE BINARY LOGS BEFORE NOW();但要注意,清理前先确认没有从库依赖被清理的日志,否则从库追上来的时候找不到路径,直接复制中断。长期:约束开发侧的更新方式,大批量数据变更要分批执行;设置 binlog_row_image = MINIMAL,ROW 格式下只记录被修改的列;根据业务调整 expire_logs_days,别一直凭默认值。
5.2 从库复制中断的典型场景
从库常见的复制中断错误就两类:
第一类是 SQL 线程报 1062 Duplicate entry(主键冲突)。原因大多数是有人在从库上手工插入了数据,或者从库曾经是主库,历史数据残留。第二类是报 1032 Can't find record(找不到记录)。原因通常是有人在从库手工删了数据,主库再来修改或删除该行时从库就找不到目标。
处理办法有快有慢。应急跳过:
STOP SLAVE; SET GLOBAL sql_slave_skip_counter = 1; START SLAVE;这条命令会让 SQL 线程跳过一个无法执行的事务。这只是应急,跳过的后果是该事务对应的数据变更永远丢失,从库和主库继续偏航。长期正确的方案是:找出主从数据差异产生的原因,然后把从库重新初始化,用当前主库的备份重搭。数据一致性这种东西,靠跳错是补不回来的。
5.3 主从连接没问题,但延迟越拉越大
IO 线程和 SQL 线程都显示 Yes,但 Seconds_Behind_Master 数值持续上涨。这种延迟极少数是网络问题,多数是主库产生了大量 binlog 而单线程重放跟不上。应对方案:
第一,确认主库大事务确实结束,如果还有大批量数据在跑,延迟会自然恢复。第二,启用并行复制。MySQL 5.7 的并行复制基于组提交,效果明显。第三,从库弱的话,考虑给从库加索引、加内存,或者把读流量分流到多个从库。
5.4 问题速查表
我把这些常见问题整理成一张表,环境里出事的时候对着查:
| 问题现象 | 可能原因 | 处理办法 |
|---|---|---|
| binlog 文件占满磁盘 | 大事务或保留天数太长 | 查大事务,FLUSH LOGS 后 PURGE,调整保留策略 |
| IO 线程显示 No | 复制账号权限不对、主库地址写错、防火墙拦截 | 检查账号 GRANT REPLICATION SLAVE,telnet 端口 |
| SQL 线程显示 No | 主从数据不一致 | 跳过后重建从库,优先重建 |
| 延迟持续增大 | 大事务、无并行复制、从库弱 | 拆事务,开并行复制,优化从库 |
| mysqldump 备份期间锁表 | 表是 MyISAM,或没加 --single-transaction | 换 InnoDB,加 --single-transaction |
| mysqlbinlog 导出内容看不懂 | ROW 格式未解码 | 加 --base64-output=decode-rows -vv |
| 备份恢复后存储过程/触发器丢失 | 导出时没带 --routines --triggers | 显式加参数重新备份 |
5.5 一点实战心得
最后分享一个我自己一直坚持的习惯:在每一次重要数据库操作之前,先手动执行一次日志滚动,把当前 binlog 文件完结,生成一个新的文件。比如要做一次大批量数据订正、或者删除一部分历史数据,操作前先:
FLUSH BINARY LOGS;这样操作前后的 binlog 会被分到不同文件里。将来如果操作出了问题,你只需要回放新文件里的内容,不需要在旧文件里翻来翻去找起始位置,操作边界非常清楚。这个习惯帮我省了太多次的麻烦。
另外一个建议是,不要把备份脚本和执行了就当完成任务。每隔一阵做一次“备份恢复演练”,把全量备份恢复到临时库,再验一下 binlog 回放流程,确认整套方案是通的。数据安全不是靠配置出来的,是靠一次次真实的恢复演练磨合出来的。等到现实里真的出事再第一次走恢复流程,那种紧张状态下还能不能冷静操作,谁也不敢保证。所以,趁现在业务没事,把你手里的备份脚本和这台库的 binlog,完整地恢复一遍试试。你会发现流程里有很多小细节,比任何文章里写的都重要。