MySQL跨库同步应基于binlog位点或GTID实现增量,而非时间字段;需开启ROW格式与FULL row image,精准过滤table map event定位库表,记录position/GTID续同步,捕获1062错误显式处理冲突。
MySQL跨库同步新增数据,别用mysqldump增量导出
用mysqldump --where加时间条件“模拟增量”,实际会漏数据:事务提交顺序和update_time不一致、insert后立刻update导致时间戳被覆盖、没主键的表根本没法靠时间字段判断。真正能落地的增量依赖的是 binlog 位点或 gtid,不是应用层时间字段。
实操建议:
- 源库必须开启
binlog_format = ROW,binlog_row_image = FULL(否则 UPDATE 只记主键,收不到新值) - 确认
server_id唯一且非零,否则 MySQL 不写 binlog event - 避免用
SELECT ... INTO OUTFILE或触发器模拟同步——锁表、性能差、无法保证一致性
用mysqlbinlog解析并过滤指定库的 INSERT 事件
mysqlbinlog 是最轻量、无需额外组件的方案,但默认输出含所有库操作,必须精准过滤。关键不是靠库名字符串匹配(比如CREATE DATABASE),而是解析 event header 中的 db_name 字段——这需要加 --base64-output=DECODE-ROWS -v 并配合 grep -A 20 "Table_map:" 定位目标库的 table map event,再往后找对应 Write_rows:。
常见错误现象:
- 直接
mysqlbinlog | grep INSERT→ 匹配到注释、日志语句、其他库的 SQL 回放语句,误判率极高 - 忽略
--skip-gtids导致 GTID event 干扰解析流,尤其在从库上执行时 - 未用
--read-from-remote-server远程拉取 binlog,却手动拷贝了正在写的 active binlog 文件 → 文件被截断或内容不完整
基于SHOW BINLOG EVENTS定位起始位置比猜时间更可靠
用 SELECT MAX(created_at) FROM target_db.table 反查时间再去找 binlog,本质是“时间漂移陷阱”:主库时钟不准、事务延迟提交、批量导入跳过时间字段。正确做法是记录上次同步成功的 binlog 文件名 + position(或 GTID set),下次从那里继续。
实操建议:
- 首次同步完,立刻执行
SHOW MASTER STATUS记下File和Position,存到配置文件或数据库里 - 每次同步前先
SHOW BINLOG EVENTS IN 'mysql-bin.000012' LIMIT 1,1验证 position 是否可读(防止被 purge) - 如果启用了 GTID,优先用
SELECT @@GLOBAL.gtid_executed+GTID_SUBSET()判断是否已包含,比 position 更容错
目标库写入失败时,INSERT IGNORE 和 ON DUPLICATE KEY UPDATE 选哪个?
跨库同步不是纯追加,常因主键冲突、唯一索引重复报 ERROR 1062。用 INSERT IGNORE 会静默丢弃整条记录,连 warning 都不报;而 ON DUPLICATE KEY UPDATE 虽可控,但若源表有多个唯一键,它只响应第一个触发的冲突,行为不可预测。
更稳的做法:
- 同步脚本里捕获
ERROR 1062,解析错误信息中的Duplicate entry 'xxx' for key 'yyy',明确是哪个键冲突 - 对业务关键字段(如
status,updated_at)做显式UPDATE,不依赖ON DUPLICATE的隐式逻辑 - 禁止在目标库删数据——同步链路没有反向 DELETE 机制,删了就再也补不回来
binlog 解析和重放之间永远存在微小窗口,主键重复只是表象,真正要盯住的是位点是否连续、GTID 是否跳跃、目标库是否有未提交事务阻塞写入。这些细节不打日志、不监控,跑一周后才发现漏了三天数据,就不是脚本能解决的事了。










