现场是这样的。MySQL 8.0.32 上用 mysqldump --single-transaction 做每日全备,某天恢复发现订单表缺了几百行,备份期间正好有人给该表加了一个索引,导出的结构和数据不在同一个快照里。

中间走过弯路,把「脚本里同时加了 lock-tables,和 single-transaction 冲突后退化成锁表」当成主因,改完现象没变。

原因不止一个,得分开看。single-transaction 只保证 InnoDB 数据一致,DDL 会隐式提交并重建表;备份期间 ALTER TABLE 让表重建,备份线程读到的是新结构和旧快照;在从库上备份,relay log 的应用位置和快照时机对不上。

先确认备份参数有没有互相打架

mysqldump --help | grep -E 'single-transaction|lock-tables|no-tablespaces'

接着查备份窗口内有没有 DDL

mysqlbinlog --start-datetime='2026-11-10 02:00:00' --stop-datetime='2026-11-10 03:00:00' mysql-bin.000123 | grep -i 'alter table'

然后换成物理备份避开 DDL 影响

xtrabackup --backup --target-dir=/data/backup/full --user=backup --password=***

等前面都确认过,再核对恢复后的结构与行数

mysql -e "SELECT COUNT(*) FROM orders" && mysql -e "SHOW CREATE TABLE orders\G" | head -20

恢复的判定其实很直接。新备份恢复后订单表行数与源库一致,表结构版本相同,备份日志里没有 DDL 冲突告警。

容易忽略的是,single-transaction 挡不住 DDL,发版窗口和备份窗口要错开;大表加索引在 8.0 也不等于不重建,占的空间一样得算。监控比日志更早给出信号,事后回看经常能提前半小时发现异常。

作者 9823189