4. Canal与Maxwell实战

聊到MySQL的实时数据同步,Binlog是绕不开的基石。而Canal和Maxwell,就是站在这个基石上的两个主流工具。我最早接触Canal是在2018年,那时候公司要做订单系统的实时异构同步,从MySQL同步到Elasticsearch。当时选型调研了一圈,最后在Canal和Maxwell之间反复横跳。今天我就把这两个工具掰开揉碎了讲,包括它们怎么解析Binlog、各自的架构设计,以及我踩过的坑。

4.1 MySQL Binlog解析:CDC的起点

Binlog说白了就是MySQL的“操作日记”。每次数据变更——INSERT、UPDATE、DELETE——都会被记录到Binlog里。Canal和Maxwell的本质工作,就是伪装成MySQL的从库,去拉取这份日记。

Binlog有三种格式:

格式 特点 适用场景
STATEMENT 记录SQL原文 简单查询,但主从数据可能不一致
ROW 记录每行变更前后的数据 CDC首选,数据精确
MIXED 自动切换STATEMENT和ROW 兼顾性能和准确性

重要提醒:做CDC时,Binlog格式必须设置为ROW。我见过有人用STATEMENT做同步,结果一条UPDATE影响10万行,Binlog里只记了一条SQL,下游根本没法还原每行的变化。血的教训。

Binlog的解析流程大致是这样的:

  1. 连接MySQL:伪装成Slave,发送dump请求。
  2. 接收Binlog事件:MySQL主库把Binlog事件推送给客户端。
  3. 解析事件:从二进制数据中提取表名、操作类型、字段值。
  4. 生成变更消息:把解析结果封装成JSON或其他格式,发送到消息队列或下游。

嗯,这里要注意,Binlog事件是二进制的,直接看是乱码。Canal和Maxwell都封装好了解析逻辑,你不需要自己写解析器。但理解底层原理,对排查问题很有帮助。

4.2 Canal架构:阿里巴巴的“管道”

Canal是阿里巴巴开源的项目,名字取自“管道”的意思。它的架构设计很清晰,我画了一张图帮你理解:

Canal 架构图 MySQL 主库 Canal Server Canal Instance 1 Canal Instance 2 Canal Instance N Kafka RocketMQ 自定义Sink Canal Server 管理多个 Instance,每个 Instance 对应一个 MySQL 数据源。 Instance 内部包含 EventParser、EventSink、EventStore 三个核心组件。

Canal的核心组件有三个:

  • EventParser:负责连接MySQL,拉取Binlog事件。它会伪装成Slave,发送dump请求。
  • EventSink:把解析后的事件进行过滤、转换、路由。你可以在这里做字段映射、数据脱敏。
  • EventStore:内存存储,暂存解析后的事件。下游消费端从这里拉取数据。

我个人习惯把Canal部署成集群模式。因为单点故障太常见了,我曾经有一次Canal进程挂了,Binlog积压了3个小时,业务方疯狂打电话。后来我用了Canal的HA方案,配合ZooKeeper做选主,再也没出过类似问题。

小技巧:Canal支持增量订阅和全量订阅。做全量初始化时,可以先关闭Binlog解析,用JDBC直连MySQL导出全量数据,再开启增量同步。这样能避免全量期间Binlog积压过多。

4.3 Maxwell架构:轻量级的“麦克斯韦”

Maxwell的名字来自物理学家麦克斯韦,寓意“电磁波一样快速传播数据”。它的架构比Canal更轻量,没有复杂的Server-Instance分层,就是一个独立的Java进程。

Maxwell 架构图 MySQL 主库 Maxwell 进程 Binlog连接器 Schema存储器 消息生成器 Kafka / Kinesis Maxwell 没有复杂的组件分层,一个进程搞定所有。 它依赖 MySQL 的 server_id 和 binlog_position 来记录消费进度。

Maxwell的设计哲学是“简单”。它把解析后的Binlog事件直接转换成JSON格式,输出到Kafka、Kinesis或stdout。你想想看,如果你只需要把MySQL数据同步到Kafka,Maxwell一行命令就能搞定:

bin/maxwell --user='root' --password='123456' --host='127.0.0.1' \
    --producer=kafka --kafka.bootstrap.servers='localhost:9092' \
    --kafka_topic=my_topic

嗯,就这么简单。我曾经在一个小项目里用Maxwell,从部署到跑通数据,只花了20分钟。Canal的话,配置要复杂一些,需要配置Instance、ZooKeeper、MQ连接等。

4.4 Canal vs Maxwell:怎么选?

这两个工具我都深度用过,下面这张表是我自己的经验总结:

对比维度 Canal Maxwell
部署复杂度 中等,需要配置Instance、ZooKeeper 简单,单进程启动
消息格式 protobuf,可自定义序列化 JSON,开箱即用
下游支持 Kafka、RocketMQ、RabbitMQ、自定义 Kafka、Kinesis、stdout
HA方案 内置,依赖ZooKeeper 需外部实现,如Kafka Connect
DDL支持 支持,可同步表结构变更 支持,但需要额外配置
性能 高,单实例可支撑万级TPS 中等,适合千级TPS场景
社区活跃度 高,阿里巴巴维护 中等,Zendesk开源

避坑指南:我曾经在一个高并发场景下用Maxwell,Binlog事件量达到每秒8000+,Maxwell的JSON序列化成了瓶颈,CPU飙升到90%。后来换成Canal的protobuf格式,CPU降到了40%。如果你的TPS超过5000,我建议优先考虑Canal。

选型建议其实很简单:

  • 小团队、快速验证、数据量不大:选Maxwell。部署快,配置少,JSON格式方便调试。
  • 大团队、高并发、需要复杂路由:选Canal。性能好,支持多种MQ,HA方案成熟。
  • 需要同步DDL(表结构变更):两个都支持,但Canal更完善。
  • 需要自定义数据转换:Canal的EventSink可以写Java代码做任意处理,Maxwell只能通过Kafka Connect做后处理。

你想想看,如果你的下游是Elasticsearch,需要把MySQL的JOIN查询结果扁平化,Canal的EventSink里写一段转换逻辑就能搞定。Maxwell的话,你得在Kafka Consumer里做,多了一层。

4.5 实战:快速搭建Canal同步MySQL到Kafka

最后,我分享一个我常用的Canal快速搭建步骤。假设你已经有了MySQL和Kafka:

  1. 开启MySQL Binlog:修改my.cnf,设置log-bin=mysql-bin和binlog-format=ROW。
  2. 创建Canal用户:CREATE USER canal IDENTIFIED BY 'canal'; GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
  3. 下载并解压Canal:从GitHub Releases下载canal.deployer-1.1.6.tar.gz。
  4. 配置Instance:编辑conf/example/instance.properties,设置MySQL地址、用户名、密码。
  5. 配置MQ:编辑conf/canal.properties,设置canal.serverMode=kafka和Kafka地址。
  6. 启动Canal:sh bin/startup.sh。

启动后,你可以在Kafka里看到example这个Topic,里面就是MySQL的变更数据。格式是protobuf,你可以用Canal提供的Java客户端解析。

小技巧:第一次启动Canal时,它会从最新的Binlog位置开始消费。如果你需要从某个历史时间点开始同步,可以在instance.properties里设置canal.instance.master.journal.name和canal.instance.master.position,指定Binlog文件名和偏移量。

好了,Canal和Maxwell的核心内容就这些。记住,工具只是手段,理解Binlog的原理才是根本。下次遇到同步延迟、数据丢失的问题,你就能从底层去排查了。