go-mysql-elasticsearch 因依赖已弃用的 go-mysql v1 和 olivere/elastic v5/v6,不兼容 ES 7.10+ 的无 type 设计及协议变更,且缺乏 DDL 处理、事务一致性、可靠 position 更新机制,导致 panic、数据丢失与同步中断。

Go-MySQL-Elastic 不是官方库,也不再维护,直接用它会导致同步中断、数据丢失或 panic,不建议在生产环境使用。
为什么 go-mysql-elasticsearch 会失败
这个项目(GitHub 上的 siddontang/go-mysql-elasticsearch)依赖已弃用的 go-mysql v1 和老版本 olivere/elastic(v5/v6),而现代 Elasticsearch(v7.10+)已移除 type 概念,且不再支持其底层使用的 Transport 层协议;同时它无法处理 DDL 变更、缺失事务一致性保障、binlog position 更新不可靠。
- 常见错误现象:
panic: runtime error: invalid memory address(因go-mysql解析 row event 时字段数不匹配) - 同步延迟波动大:单 goroutine 消费 binlog + 同步写 ES,无批量、无重试、无背压控制
- ES 写入失败后 position 仍前进,造成永久性数据丢失
替代方案:用 go-mysql + olivere/elastic/v7 自建管道
核心思路是拆解原项目逻辑:用 go-mysql 拉取 binlog,自己解析 RowEvent,再构造 bulk 请求发给 ES。关键在于控制好 position 提交时机和错误回退。
- 必须在 ES bulk 成功后才调用
es.UpdateCheckpoint()(或等价的 position 持久化) - 对
INSERT/UPDATE/DELETE分别映射为 ES 的index/update/delete操作,注意UPDATE需提取主键作为_id - 避免直接用表名当 index 名:MySQL 表名可能含下划线或大小写,ES index 名必须小写、不能以
_开头、不能含特殊字符 —— 建议统一转成db_table格式并小写 - 字段类型要对齐:MySQL 的
DATETIME→ ES 的date,TINYINT(1)→boolean,否则 bulk 会 400
如何处理 DDL 和 schema 变更
go-mysql 能捕获 QueryEvent,但不会自动更新 ES mapping。你得自己监听 CREATE TABLE/ALTER TABLE,并触发 mapping 更新(需手动调用 ES API)。
- DDL 事件必须阻塞 binlog 消费:先停 consumer,再发
PUT /index/_mapping,成功后再恢复 - 新增字段可设
"dynamic": "true",但删除字段不会自动从 mapping 移除 —— ES 不支持删 field,只能 reindex - 推荐做法:所有表提前定义好 strict mapping,DDL 变更走发布流程,禁止线上直接
ALTER
性能与可靠性要点
真实微服务场景下,单实例同步扛不住高并发写入,必须加缓冲和降级能力。
- binlog event 先写入本地
leveldb或bolt(而非内存 channel),防止进程 crash 丢 position - ES 写入必须用
bulk(每 100–500 条一批),并设置timeout和retries;失败时整个 batch 回滚,重试前 sleep 指数退避 - 监控三项指标:binlog lag(对比
SHOW MASTER STATUS)、ES bulk error rate、checkpoint 文件 last modified time - 不要依赖 MySQL 的
server_id做唯一标识 —— 多个同步任务共用同一 server_id 会导致 binlog 位置错乱
真正难的不是把数据从 MySQL 推到 ES,而是让每条变更都“恰好一次”落地,且能经受住网络抖动、ES 集群滚动重启、MySQL 主从切换。这些细节没兜住,准实时就变成“偶尔实时”。


















