Canal 组件
前言
做业务系统时经常会遇到这样的需求:数据库里的一行数据变了,下游的缓存、搜索引擎、数据仓库也要跟着变。靠业务代码双写容易漏,靠定时任务全量比对又太重。这类问题有一个更优雅的解法——直接订阅数据库的变更日志,这也是我整理这篇 Canal 笔记的原因。
Canal 是阿里巴巴开源的一款 基于 MySQL Binlog 的数据实时订阅与消费组件,常用于实现数据库变更数据捕获(CDC,Change Data Capture)。
在实际业务场景中,数据库中的数据变更往往不仅仅用于业务读写,还可能需要同步到其他系统,例如:
- 实时数据同步(数据库 → 数据仓库)
- 数据变更推送到 MQ(Kafka / RocketMQ)
- 构建实时数据分析或监控系统
- 搜索引擎数据同步(如 Elasticsearch)
这些场景的共同点是:下游系统关心的不是"数据库现在长什么样",而是"数据发生了哪些变化"。如果让业务代码在每次写库后手动通知下游,不仅侵入性强,还很难保证和数据库事务的一致性——写库成功但通知失败,数据就悄悄不一致了。而 Binlog 是 MySQL 自身保证写入的日志,以它为数据源天然避开了双写问题。
为了实现这些能力,Canal 通过 模拟 MySQL Slave 的方式订阅 Binlog 日志,解析出数据库的增删改操作,并将这些变更数据实时推送给下游消费系统,从而实现数据的准实时同步。
工作原理
Canal 的实现方式借用了 MySQL 主从复制的机制。正常的主从复制流程是:Slave 向 Master 发起 dump 请求,Master 将 Binlog 事件持续推送给 Slave,Slave 再重放这些事件完成数据同步。
Canal 做的事情,就是把自己"伪装"成一个 Slave:
- 向 MySQL 发送 dump 协议请求,和真实从库使用同一套交互方式;
- MySQL 把 Binlog 事件推送给 Canal,Canal 解析二进制日志,还原出每一行数据的变更内容;
- 解析后的结构化事件交给下游消费——既可以由客户端通过 TCP 主动拉取,也可以直接投递到 Kafka / RocketMQ 等消息队列。
对 MySQL 来说,Canal 就是一个普通的从库,不需要在数据库侧安装任何插件,这也是这种方案侵入性低的原因。
要让 Canal 正常订阅,MySQL 侧需要满足几个前提条件:
# my.cnf 中的关键配置
log-bin=mysql-bin # 开启 Binlog
binlog-format=ROW # 必须为 ROW 模式,才能拿到每一行变更前后的完整数据
server_id=1 # 主从体系中每个节点的唯一标识,不能与 Canal 配置的 slaveId 冲突
其中 Binlog 格式必须是 ROW 模式。STATEMENT 模式记录的是 SQL 语句本身,无法还原出每行数据的具体变化;只有 ROW 模式会记录行级别的前后镜像,CDC 场景才有意义。此外,Canal 连接 MySQL 使用的账号需要具备 REPLICATION SLAVE、REPLICATION CLIENT 权限,和真实从库的要求一致。
常见 CDC 组件对比
目前市面上常见的 CDC 组件主要包括:
- Canal
- Debezium
- Flink CDC
Canal 目前只支持MySQL数据库。5.x 8.x 版本。
GitHub - alibaba/canal: 阿里巴巴 MySQL binlog 增量订阅&消费组件
这类组件的核心工作机制基本一致:通过解析数据库的 Binlog 日志,获取数据变更内容以及具体的操作类型(INSERT / UPDATE / DELETE),并将这些变更数据转化为结构化事件供下游系统消费。
三者的差异主要在生态定位上:Debezium 构建在 Kafka Connect 之上,支持 MySQL、PostgreSQL 等多种数据库,适合已有 Kafka 体系的团队;Flink CDC 把变更捕获直接嵌入 Flink 流计算,捕获和加工在同一个作业里完成;Canal 则更轻量,专注于 MySQL 这一个方向。
需要注意的是,Canal 目前主要支持 MySQL 数据库(5.x 与 8.x 版本),因此在 MySQL 生态中被广泛用于构建实时数据同步与数据分发系统。如果技术栈是纯 MySQL,不想引入 Kafka Connect 或 Flink 这样的重依赖,Canal 是比较务实的选择。
高可用说明
在高可用方面,Canal 提供了 集群部署模式。在集群架构中,每个 Service 节点负责管理多个同步任务实例(Instance),并通过任务分发机制实现负载分担。
集群模式依赖 ZooKeeper 做协调:多个 Server 节点会去争抢同一个 Instance 的运行权,同一时刻只有一个节点真正在跑该 Instance,其余节点处于 standby 状态。运行节点故障后,standby 节点通过 ZooKeeper 感知并接管任务,同时消费位点(即 Binlog 的消费进度)也记录在 ZooKeeper 中,接管方可以从上一个位点继续消费,避免数据丢失或大量重复。
不过在实际运行过程中,偶尔可能会因为网络波动、数据库连接异常或资源限制等原因,导致某些同步任务实例终止。因此在生产环境中,Canal 通常会配合 自动重启机制或运维监控系统,以保证任务能够在异常情况下自动恢复运行。
踩坑与注意
结合原理,有几个点在落地前最好先想清楚:
-
Binlog 格式与保留时间。上游库必须是 ROW 模式;同时 Binlog 保留时间要足够长,否则 Canal 停机时间一长,位点对应的日志已被清理,任务就无法续传,只能重新初始化。
-
消费语义是至少一次。故障切换或重启后,可能会重复投递一小段变更事件,下游消费端需要按主键做幂等处理,不能假设每条变更只到达一次。
-
顺序性依赖分区策略。投递到 MQ 时,如果多张表或多个主键的变更被打散到不同分区,消费顺序就可能和 Binlog 顺序不一致。对顺序敏感的场景,通常按表名或主键做分区路由。
-
Instance 假死问题。任务实例有时并不会干净地退出,而是停在某个位点不再推进。监控不能只看进程存活,还要看位点是否持续前进,以及与数据库当前 Binlog 位置的延迟。
上线前可以做一次"断链演练":手动 kill 掉正在运行的 Server 节点,观察 standby 是否按预期接管、位点是否续传成功,比出了故障再验证要从容得多。
小结
Canal 的思路并不复杂:伪装成 MySQL 从库,用标准复制协议订阅 Binlog,把行级变更还原成结构化事件供下游消费。它对数据库零侵入、部署轻量,在纯 MySQL 技术栈里做实时同步和数据分发很合适。工程上需要重点关照的,是 ROW 格式与 Binlog 保留策略、下游的幂等消费,以及基于位点推进情况的监控——这些做扎实了,链路的稳定性才有保障。
评论 / COMMENTS