pglogrepl 是 postgresql 增量同步最稳方案,需设 wal_level=logical、用 replication 用户、基于 lsn 启动复制、解析二进制消息、异步处理防 wal 回收,并在目标库提交后持久化 checkpoint。

用 pglogrepl 做 PostgreSQL 增量同步最稳
PostgreSQL 的逻辑复制协议(Logical Replication)是目前 Golang 做增量同步最可靠的基础,pglogrepl 库封装了底层协议细节,比轮询 updated_at 或解析 WAL 文件更准、更轻、不锁表。
常见错误是直接用 SELECT * FROM table WHERE updated_at > ? 拉数据——它漏删、漏更新、跨事务不一致,尤其在高并发写入时错得隐蔽。
- 必须开启 PostgreSQL 的
wal_level = logical,并创建有REPLICATION权限的用户 - 同步起点用
pglogrepl.StartReplication传入 slot 名和起始 LSN,不是时间戳或自增 ID - 收到的
pglogrepl.Message需按类型区分:pglogrepl.InsertMessage、pglogrepl.UpdateMessage、pglogrepl.DeleteMessage,字段名和值都是二进制编码,得用pglogrepl.ParseMessage解析 - 别在消息处理里做耗时操作(比如 HTTP 请求),否则 LSN 提交延迟,主库 WAL 可能被回收,导致断连重放失败
MySQL 增量同步优先选 go-mysql + binlog dump 协议
MySQL 没有标准逻辑复制 API,go-mysql 是目前最成熟的 binlog 解析方案,它模拟从库行为,主动请求 binlog 流,比用 SELECT ... FOR UPDATE 或触发器靠谱得多。
典型翻车点:用 SHOW MASTER STATUS 查当前位点后手动 SELECT,结果中间插入了未提交事务,或者位点被 purge 掉。
- 初始化要先调
BinlogSyncer.StartSync,传入binlog_file和binlog_pos,不能只靠Exec查询 -
RotateEvent必须处理——它意味着 binlog 切文件,不更新位点会导致下个文件读错 - DDL 事件(
QueryEvent含CREATE TABLE)默认被跳过,如需同步结构变更,得显式设置IncludeDDL: true - MySQL 8.0+ 默认启用
binlog_row_image = FULL,但若目标库是 5.7,注意MINIMAL模式下UpdateMessage缺少旧值,无法做幂等更新
同步失败后怎么安全续传
续传不是简单“从上次位置再连”,核心是 LSN(PostgreSQL)或 binlog position(MySQL)的持久化时机——必须在目标库成功写入且事务提交后,才更新本地 checkpoint。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
否则会出现“数据写了,位点没记,重启后重复写”或“位点记了,数据没写,重启后丢数据”。
- PostgreSQL:用
pglogrepl.SendStandbyStatusUpdate提交 LSN,但仅当pglogrepl.ReplicationMessage对应的目标写入确认后才调用 - MySQL:把
BinlogEvent.Header.LogPos写进一张带FOR UPDATE的小表,用UPDATE checkpoint SET pos = ? WHERE id = 1确保原子性 - 避免用本地文件存位点——进程崩溃时文件写入可能未刷盘;也别用内存变量,重启就丢
- 每次启动先查 checkpoint 表,再连主库校验该位点是否 still available(MySQL 用
SHOW BINLOG EVENTS IN ? FROM ? LIMIT 1,PG 用pg_replication_slots视图)
目标库写入怎么避免主键冲突和重复
增量同步本质是重放操作,但目标库可能已有数据,或上游重发消息,所以不能无脑 INSERT。
最易忽略的是:MySQL 的 REPLACE INTO 和 PostgreSQL 的 ON CONFLICT DO UPDATE 看似能防重,但它们不保证顺序——两个并发 UPDATE 可能相互覆盖,最终状态不可控。
- 对单行操作,用带条件的更新:
UPDATE t SET a=? WHERE id=? AND version ,配合版本号或时间戳字段 - PostgreSQL 推荐用
INSERT ... ON CONFLICT (id) DO UPDATE SET ... WHERE excluded.updated_at > t.updated_at,利用excluded引用新值 - MySQL 5.7+ 支持
INSERT ... ON DUPLICATE KEY UPDATE,但注意VALUES(col)在多值 INSERT 中取的是第一条记录的值,别误用 - 所有写操作必须包在事务里,且隔离级别至少
READ COMMITTED,否则幻读会导致判断逻辑出错
LSN 和 binlog position 的可靠性依赖主库配置和网络稳定性,但真正难的是:如何让目标库的状态收敛到和源库一致。这要求每一步——拉取、解析、写入、位点提交——都形成闭环,缺一不可。漏掉任一环,同步就会慢慢漂移,直到某天发现数据对不上,却找不到哪一次出了问题。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










