3. Canal入门:从原理到架构,搞懂增量同步的“心脏”
说实话,我第一次接触Canal的时候,心里是有点犯嘀咕的。一个中间件,凭什么能精准抓到MySQL的每一笔变更?后来我深入扒了它的源码,又在自己负责的电商订单同步项目里踩了几个坑,才算真正搞明白。今天咱们就把Canal的底裤扒干净,从它是什么、怎么工作,到内部那几个核心组件,一个一个说清楚。
3.1 Canal是什么?
Canal,说白了就是一个MySQL binlog的“翻译官”和“搬运工”。它把自己伪装成一个MySQL的从库(Slave),然后去拉取主库(Master)的binlog日志。拉过来之后,再把这些二进制日志解析成我们能看懂的数据变更事件——比如哪一行插入了、哪一行更新了、哪一行删除了。
我习惯把它比作一个“数据窃听器”。它不直接参与业务,就蹲在数据库旁边,默默记录所有变化,然后转手丢给下游的消费者(比如Kafka、RocketMQ、或者直接是另一个数据库)。
核心价值: Canal让“实时增量同步”这件事变得简单可靠。你不需要在业务代码里写一堆双写逻辑,也不需要去解析复杂的binlog格式。Canal帮你把脏活累活全干了。
3.2 Canal的工作原理:模拟Slave,拉取binlog
它的工作流程其实不复杂,我画个图你就明白了。
具体步骤是这样的:
- Canal启动,向MySQL Master发送dump请求,并带上自己模拟的Slave的serverId。
- Master开始推送binlog,Canal接收这些二进制日志。
- 解析日志,把binlog里的“事件”转换成Canal内部的数据结构(比如Entry、RowChange)。
- 投递到Store,解析好的事件先暂存在内存或磁盘里。
- 消费,下游的客户端(或者MQ)从Store里拉取数据,完成同步。
避坑指南: 我曾经在一个高并发场景下,忘了给Canal配置独立的MySQL账号,结果binlog的dump权限没开,Canal一直连不上。记住,MySQL那边必须给Canal授权REPLICATION SLAVE和REPLICATION CLIENT权限。
3.3 Canal的架构组件:Server、Instance、EventParser、EventSink、EventStore
Canal的架构设计得很精巧。它把整个流程拆成了几个独立的组件,每个组件各司其职。我习惯把Canal比作一个“数据加工厂”,咱们看看这个工厂里都有哪些车间。
3.3.1 Canal Server
Server是Canal的“大管家”。它是一个Java进程,负责启动、管理、监控所有的Instance。你想想看,一个Server可以同时跑几十个Instance,每个Instance对应一个数据库或者一张表。Server还负责提供网络服务,让客户端可以连上来订阅数据。
我习惯把Server理解成一个容器。它不干具体的活,但它知道哪个Instance在哪儿,状态怎么样,出了问题怎么重启。
3.3.2 Canal Instance
Instance才是真正干活的“工人”。一个Instance代表一个数据同步任务。它内部包含了Parser、Sink、Store这三个核心组件。每个Instance都有自己的配置,比如要监听哪个数据库、哪些表、binlog的起始位置等等。
举个例子,我在做订单库同步的时候,就为订单库单独建了一个Instance,为商品库建了另一个Instance。这样互不干扰,一个挂了不影响另一个。
3.3.3 EventParser
Parser是Canal的“侦察兵”。它的任务就两个:拉取binlog + 解析binlog。
- 拉取:它模拟MySQL Slave的协议,向Master发送dump请求。这里有个细节,Parser会记录自己消费到的binlog位置(position),这样即使Canal重启,也能从断点处继续拉,不会丢数据。
- 解析:把binlog里那些二进制数据,解析成Canal定义的Entry对象。Entry里包含了表名、事件类型(INSERT/UPDATE/DELETE)、字段名、字段值等等。
注意: Parser是性能瓶颈的高发区。我曾经遇到过一个场景,MySQL主库的binlog量特别大(每秒几百MB),Parser解析不过来,导致数据延迟越来越大。后来我调整了Parser的线程数和缓冲区大小,才把问题解决。
3.3.4 EventSink
Sink是Canal的“质检员和分拣员”。Parser解析出来的原始事件,会先送到Sink这里。Sink会做几件事:
- 过滤:根据配置,只保留你关心的表或库的事件。比如你只想要
order表的数据,那user表的事件就直接丢弃。 - 转换:对事件做一些简单的处理,比如字段类型转换、脱敏等。
- 路由:决定把事件送到哪个Store里。一个Instance可以有多个Store吗?嗯,理论上可以,但一般一个Instance配一个Store就够了。
Sink的设计其实很巧妙。它把“解析”和“存储”解耦了。Parser只管解析,Sink只管处理,互不干扰。
3.3.5 EventStore
Store是Canal的“临时仓库”。解析好的事件不能一直放在内存里,得有个地方暂存一下,等着下游来消费。
Canal的Store默认是基于内存的环形队列(RingBuffer)。为什么用环形队列?因为性能高啊!无锁设计,读写效率极快。我见过一些团队把Store改成基于磁盘的,为了应对极端情况下的数据堆积。但说实话,大部分场景下内存Store就够了。
| 组件 | 职责 | 我眼中的角色 |
|---|---|---|
| Canal Server | 管理Instance,提供网络服务 | 大管家 |
| Canal Instance | 单个同步任务的执行单元 | 工人 |
| EventParser | 拉取并解析binlog | 侦察兵 |
| EventSink | 过滤、转换、路由事件 | 质检员 |
| EventStore | 暂存解析后的事件 | 临时仓库 |
个人经验: 刚开始用Canal的时候,我总喜欢把所有表都放在一个Instance里。后来发现,一旦某张表的数据量暴增,整个Instance的消费速度都会被拖慢。我的建议是:按业务模块拆分Instance。核心业务一个Instance,非核心业务另一个,这样能有效隔离故障。