truncate在逻辑备库失败是设计使然而非bug:row模式下不写binlog故不复制;statement/mixed模式下可能因外键、权限等报错;跳过需谨慎校验,长期应禁用truncate改用可审计的delete+auto_increment重置。
truncate在逻辑备库直接失败,不是bug是设计
mysql逻辑复制(row模式)下,truncate table默认不写入binlog,主库执行后,备库根本收不到这条语句——所以不是“同步失败”,而是“压根没同步”。但如果你用的是statement或mixed模式,又开了binlog_format=statement,那它会被记录为sql文本发过去;而备库执行时若遇到外键、权限、表不存在等问题,就会卡住报错,比如:error 1022 (23000): can't write; duplicate key in table 或 error 1217 (hy000): cannot delete or update a parent row: a foreign key constraint fails。
- 先确认当前模式:
SHOW VARIABLES LIKE 'binlog_format'; - 再查复制状态:
SHOW SLAVE STATUS\G,重点看Seconds_Behind_Master和SQL_Errno - 逻辑备库对DDL本就保守,
TRUNCATE这种隐式提交+重置自增的DDL,在备库缺乏上下文时极易中断
跳过失败的TRUNCATE:只适用于临时救急
跳过不是推荐做法,而是断链后的兜底手段。一旦跳过,主备数据就出现结构性偏差:主库空表,备库还留着旧数据,后续INSERT可能因自增ID冲突、唯一键重复直接报错。
- 临时跳过单条事件:
SET GLOBAL sql_slave_skip_counter = 1;(仅适用于STATEMENT模式) - 更安全的替代方式(MIXED/ROW):
STOP SLAVE; SET GTID_NEXT='xxx-yyy-zzz:N'; BEGIN; COMMIT; SET GTID_NEXT='AUTOMATIC'; START SLAVE;,其中N是出错事务后下一个GTID序号 - 跳过之后必须立刻校验:
pt-table-checksum比对表行数、校验和,不能只看Seconds_Behind_Master=0
手动在备库执行TRUNCATE前,必须做三件事
别急着连上去敲TRUNCATE——主库能过,不代表备库环境一致。常见翻车点:外键未禁用、表被其他会话锁住、自增ID重置值与主库不一致。
- 先停复制:
STOP SLAVE; - 检查外键状态:
SELECT @@FOREIGN_KEY_CHECKS;,若为1,务必先执行:SET FOREIGN_KEY_CHECKS = 0; - 确认无活跃事务锁表:
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA='db_name' AND OBJECT_NAME='table_name';,有结果就得等或杀会话 - 执行后重置自增:
ALTER TABLE table_name AUTO_INCREMENT = 1;(主库TRUNCATE会自动做这事,备库手动执行不会)
长期方案:别让TRUNCATE进复制流
真正稳定的解法,是把TRUNCATE从线上操作中剔除。它太快、太静默、太难审计,尤其在主从架构里,相当于给复制管道塞了一颗哑弹。
- 统一改用
DELETE FROM table_name;+ALTER TABLE ... AUTO_INCREMENT = 1;组合,虽然慢,但全程可回滚、可记录、可审计 - 如确需清空大表,用分批删除:
DELETE FROM table_name ORDER BY id LIMIT 10000;,配合SLEEP(0.1)避免长事务阻塞复制 - 所有DDL变更走发布平台审批流程,禁止开发直连生产库执行
TRUNCATE——这点比任何技术补丁都管用
最麻烦的不是怎么跳过或重做,而是你得确认备库此刻的表结构、约束、触发器、分区定义,是否和主库完全一致。差一个ON UPDATE CURRENT_TIMESTAMP,或者少一个INDEX,都可能导致后续DML行为不一致。这事儿没法自动化,只能靠人盯。










