MySQL 擅长事务与精确查询,却不擅长全文检索和多维筛选;引入 Elasticsearch(下称 ES)后,MySQL 仍是写入真相源,ES 只是读模型。真正的难点不在”怎么搬数据”,而在一致性:消息乱序怎么办、重试会不会重复、全量与增量怎么衔接。本文对比主流方案并给出工程要点。

方案对比

方案 延迟 侵入性 一致性风险 适用场景
应用双写 低 高,要改业务代码 两个写操作无法同事务,失败即不一致 链路极短的小系统
定时扫库 高(分钟级) 低 依赖更新时间字段,删除难以感知 对延迟不敏感的报表
MQ 消息 低 中,业务要发消息 消息漏发或乱序 已有可靠消息设施
binlog 订阅(Canal/Debezium) 秒级 无侵入 需处理顺序与幂等 生产主流方案

双写的问题是”写 MySQL 成功、写 ES 失败”没有事务保护,补偿逻辑散落在业务代码里,时间一长必然漂移出脏数据。定时扫库实现最简单,但 WHERE update_time > ? 的方式有窗口遗漏风险(事务提交晚于扫描时刻的行会被跳过),且物理删除无法通过扫描感知。MQ 方案把双写改造成”写库 + 发消息”,配合本地消息表或事务消息保证不丢,比裸双写可靠,但业务仍要感知同步这件事。binlog 订阅让同步链路完全脱离业务代码,是目前中大型系统的主流选择。

Canal 原理:伪装成 Slave

MySQL 主从复制的本质是主库把 binlog 推给从库。Canal 利用这一点:它向主库注册为一个”假 slave”,发送 dump 协议请求,主库便像对待真从库一样持续推送 binlog 事件。Canal 解析出 INSERT/UPDATE/DELETE 的行镜像,投递到 Kafka/RocketMQ 或直接用客户端消费,消费者再把变更写入 ES。

这个架构的好处一目了然:对业务零侵入、延迟在秒级、且 binlog 天然包含删除事件。代价是要自己维护订阅位点(binlog file + position),处理 Canal 服务重启后的续传,以及监控消费延迟(lag)。

一致性挑战一:顺序性

同一行短时间内更新多次,如果事件被并发消费或走了不同分区,后到 ES 的可能是旧值。处理手段:

  • 按主键哈希分区:把同一行的所有事件固定路由到同一队列分区、同一消费线程,局部有序即可,不需要全局有序。
  • ES 侧版本控制:写入时使用 external 版本号(可用 binlog 位点或更新时间戳),ES 拒绝版本低于当前文档的写入,乱序旧值自然失效。

一致性挑战二:幂等

消费者”至少一次”投递意味着同一事件可能重复到达。对策是让写入天然幂等:用 MySQL 主键作为 ES 文档 ID,更新操作用全量覆盖式的 index 请求而不是条件拼装;删除同样按文档 ID 执行,重复删除不报错。这样无论重试多少次,终态一致。

一致性挑战三:全量与增量衔接

新索引上线或索引损坏重建时,要走全量流程。正确的姿势是”全量 + 增量并行”而不是先全量后增量:

  1. 开始全量导数前,先记录当前 binlog 位点并启动增量消费(写入目标新索引)。
  2. 全量数据导入新索引;导入期间的变更已由增量链路同步,配合版本号控制,旧的全量数据不会覆盖新的增量数据。
  3. 校验行数与抽样字段一致后,用 ES 的 alias 把读流量切到新索引,旧索引观察一段时间后下线。

Alias 切换是零停机重建的关键:应用只访问别名,不绑定物理索引名。

工程实践清单

  • mapping 先行:keyword 与 text 的选择决定能否聚合、能否全文匹配;中文字段提前选定分词器,mapping 变更多数需要重建索引。
  • bulk 批量写入:攒批提交减少 ES 段合并压力;bulk 响应要逐条检查,部分失败单独重试。
  • 死信队列:反复失败的消息进 DLQ 并告警,人工介入而不是无限重试堵塞主流程。
  • 监控三件套:Canal 消费 lag、ES bulk 拒绝率、binlog 磁盘剩余——lag 追不上时要先确认 binlog 还没被清理,否则只能回全量重建。
  • 认清边界:ES 是最终一致的读模型,资金、库存类强一致业务永远以 MySQL 为准,检索结果回表校验是常见兜底。

选型建议

  • 小系统、改动窗口有限:接受秒到分钟级延迟就用定时任务,把精力放在对账上。
  • 已有 MQ 基础设施且字段简单:MQ 消息方案够用,记得本地消息表防丢。
  • 表多、链路多、延迟要求秒级:上 Canal/Debezium + MQ,按主键分区保序,版本号防乱序。
  • 需要流式聚合清洗(多表 join 打宽后再入 ES):评估 Flink CDC,成本是架构复杂度。

小结

  • 双写胜在简单、败在一致性;binlog 订阅胜在无侵入与完整性,是生产主流。
  • Canal 本质是伪装成 slave 订阅 binlog,同步可靠性转移到”位点管理 + 消费幂等”上。
  • 顺序性靠按主键分区加版本号,幂等靠主键即文档 ID 的覆盖写。
  • 全量重建用”增量先行、alias 切换”模式,不停机也不丢变更。
  • ES 永远只是读模型,强一致业务以 MySQL 为准。