最近把用 Canal 做缓存一致性的项目放到简历上,结果面试的时候被问到:"Canal 不是已经淘汰了吗,阿里内部都不用了。"

当时我愣了一下,没答好。回来查了很多资料,也翻了不少源码,越看越觉得这个问题值得认真写一篇。


先说说 Canal 到底是什么

很多同学(包括我刚开始)看到 Canal 这个词,直接去找文档看怎么配置,跳过了"它为什么存在"这个问题。其实理解背景比记配置重要得多。

阿里为什么要造 Canal?

阿里早期在杭州和美国都有机房,两边数据要保持同步。最初的做法是在业务代码里手动处理:

// 伪代码,大概是这个意思
saveOrderToDB(order);           // 写杭州数据库
syncToUSDataCenter(order);      // 手动同步到美国机房

这种做法问题很明显——同步逻辑散落在各处业务代码里,漏写一处就出 bug,维护起来噩梦一样。

到了 2010 年,他们换了一个思路:不改业务代码,直接监听数据库的变更日志(binlog)。数据库有什么变化,日志里都有记录,读日志就能知道要同步什么,和业务代码完全解耦。这个需求一直在长,Canal 就是在这个背景下诞生的。

2014 年双十一,Canal 被正式用到天猫大促里——几千万人同时抢购,MySQL 根本扛不住这个读写量。Canal 的作用是把数据库的变更实时同步到 Redis,大部分读请求走缓存,不打数据库,压力就分散掉了。后来在阿里内部大规模推广,2017 年对外开源。


Canal 的核心原理,说白了就一句话

它伪装成 MySQL 的一个从库。

MySQL 主从复制的机制是这样的:从库向主库注册,主库就会把 binlog 源源不断地推给从库,从库拿到 binlog 之后执行,保持和主库数据一致。

Canal 就是利用了这套协议。它向 MySQL 注册,带一个合法的 server-id,声称自己是从库。MySQL 不知道对方其实是 Canal,该推的 binlog 照样推。Canal 拿到 binlog 之后,不是去同步数据,而是把它解析成结构化的变更事件,交给下游处理。

整个过程对 MySQL 完全无侵入,不需要改任何配置,也不需要装插件。

MySQL 主库
    ↓  (以为在做主从同步,正常推送 binlog)
Canal Server  (其实是在监听,拿到变更后解析)
    ↓
消息队列 (Kafka / RocketMQ)
    ↓
消费者 (拿到变更事件,删 Redis 缓存)

binlog 里记录了什么?

binlog 以 row 格式记录了每一行的变化,包含:

  • 操作类型:INSERT / UPDATE / DELETE
  • 表名和库名
  • 变更前的行数据(old row)
  • 变更后的行数据(new row)

所以 Canal 解析出来之后,下游能精确知道"用户表里 id=123 这一行被改了,name 从李四改成了张三",精确到字段级别。


为什么用 Canal 做缓存一致性?

传统方案是:写数据库的同时,在业务代码里手动删缓存。

// 传统写法,到处都要这样写
userMapper.update(user);
redisTemplate.delete("user:" + user.getId());

这有两个痛点:

第一,容易漏。 项目大了,写数据库的地方多,每个地方都要记得手动删缓存,总有漏的时候,漏了就是脏数据。

第二,分布式下有竞争条件。 高并发下,删缓存和写数据库之间有时间差,可能出现:缓存刚被删,另一个线程把旧数据又写进去了。

Canal 的做法是:业务代码只管写数据库,缓存的事不管。Canal 订阅 binlog,数据库一变,Canal 就感知到,异步通知消费者去删 Redis 里对应的 key。数据库是唯一数据来源,缓存跟着走,逻辑清晰,也不会漏。

当然,这是有代价的——从数据库写入到缓存失效,中间有一个短暂的窗口期,这段时间读缓存可能拿到旧数据。这就是所谓的最终一致性,不是强一致。如果业务对一致性要求极高(比如金融实时对账),Canal 方案不够用,那就要上分布式事务了。


回到最初的问题:Canal 过时了吗?

说 Canal 过时,原因通常是:阿里内部已经转向更内聚的方案,Canal 作为独立中间件维护成本太高,HA(高可用)搭建也比较复杂。

这个说法有一定道理,但放大来说就过了。

事实是:Canal 在业界依然被大量使用,很多中小公司生产环境跑得好好的。技术选型从来都是权衡,不是非此即彼。真正需要关心的问题是:你的系统体量下,Canal 能不能满足需求?团队有没有人能维护?这才是选型的核心。

如果真要替换,目前最成熟的替代品是 Debezium——同样基于 binlog,但社区更活跃,和 Kafka Connect 生态集成更好,功能也更丰富。


面试时怎么答这个问题?

我后来想了一下,当时应该这样回答:

"Canal 确实有它的局限性,在超大规模下维护成本比较高,HA 搭建也有一定复杂度。我在项目里选它,主要是它的 binlog 订阅机制足够清晰,能帮我理解数据库变更捕获的底层原理。如果要在生产上大规模使用,我会优先评估 Debezium,社区维护更积极,生态也更完整。"

答案的逻辑是:承认缺点,说清楚为什么当初选,再表明你知道替代方案。比直接说"我不知道它过时了"强很多。


最后说一句

学一个技术,把它的来龙去脉搞清楚,比只会用它配置更重要。Canal 这个项目,让我真正理解了 MySQL 主从复制协议、binlog 的结构、缓存一致性的几种方案和各自的取舍。这些知识不会因为 Canal 热不热门而过时。

工具可以换,底层的理解带不走。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐