用 binlog 订阅(如 Canal)做缓存/数据同步的原理与坑是什么?

结论先行:Canal 把自己伪装成 MySQL 从库,消费主库 binlog 的增量事件,再把这些变更转发给缓存、搜索引擎、异构存储等下游,让“写完 DB 再同步其它存储”从业务代码里解耦出来。这是变更数据捕获(CDC)的一种典型落地,适合最终一致可接受、又不想侵入业务的场景。

一、整体链路

  1. 业务写主库并提交事务,变更以行事件形式写进 binlog(要求 row 格式);
  2. Canal 模拟 slave 向主库发起 dump 请求,持续拉取 binlog 并解析成 Insert/Update/Delete 事件;
  3. 事件被投递到 Canal 自带 MQ/adapter,或由客户端直连消费;
  4. 下游按需消费:删除/更新缓存、同步到 ES、异构表、数仓等,消费端自己做幂等。

二、为什么要 row 格式

  • statement 格式只记录 SQL,遇到不确定函数、批量更新时回放结果可能不一致;row 格式记录每行变更前后的值,最稳定,也是增量解析的前提;
  • 使用前提是主库开启:binlog_format=ROWbinlog_row_image=FULL,并给 Canal 一个独立账号与不冲突的 server-id:
[mysqld]
server-id        = 100            # 与其它从库/Canal 实例错开
binlog_format    = ROW
binlog_row_image = FULL

三、工程要点与常见坑

  • 消费必须幂等:事件可能重复投递或重启后重放,按主键/版本号去重,或让操作天然可重入(例如删缓存、按幂等键更新);
  • 顺序性:同一行的变更要按顺序消费,通常单表单线程或按 key 哈希分区,避免“后写先到”造成旧值覆盖新值;
  • 延迟量级:拉取、解析、投递、消费整条链路存在亚秒到秒级延迟,实时性要求更高的场景不能依赖它;
  • 双写 vs CDC:业务双写侵入强、容易漏改一处;CDC 解耦干净,但要多维护一套 Canal 的高可用与位点(offset)续传;
  • 别对全库无差别订阅,先按库表白名单过滤,超大表/敏感表单独评估;
  • 下游失败要能“重放+对账”:记录消费位点,故障恢复后从位点续传,定期跑一次对账任务兜底。

四、适用场景速查

场景是否推荐 CDC理由
缓存与 DB 最终一致推荐删缓存天然幂等,解耦业务
同步数据到 ES 做搜索推荐异构存储同步的标准姿势
实时性要求毫秒级不推荐链路延迟摆在那
需要事务性强一致不推荐CDC 只保证最终一致

常见追问/记忆点

  • 追问:Canal 挂了怎么办?答:位点续传 + 消费端幂等兜底,极端情况从备份时间点重放,再配对账任务收敛。
  • 追问:和“业务里发 MQ 删缓存消息”有什么区别?答:CDC 由 DB 变更驱动,不依赖业务每条路径都发消息;两者可以叠加,CDC 兜底、MQ 提速。
  • 记忆点:伪装从库读 binlog → 解析行事件 → 幂等消费;适用“最终一致 + 秒级延迟可接受”的下游。
笔记加载中…