Canal本身不直接写目标库,仅解析binlog并暴露变更事件,必须搭配消费者程序或Kafka等中间件完成MySQL到异构系统的增量同步;其instance配置须严格匹配MySQL当前binlog文件名与position,且binlog-format必须为ROW模式,否则无法准确还原行级变更。

Canal 本身不直接写目标库,它只负责解析 binlog 并暴露变更事件;要完成「MySQL → 异构系统」的增量同步,必须搭配消费者程序或中间件(如 Kafka)来中转和投递。跳过这层,直接指望 Canal 自带写入能力,会卡在第一步。
Canal 的 instance 配置必须匹配 MySQL 的 binlog 状态
Canal 启动时会向 MySQL 请求从指定 binlog filename 和 position 开始 dump。如果配置的起始点已过期(比如 MySQL 的 expire_logs_days 清理了旧日志),instance 就会报 Could not find first log file name in binary log index file 或卡在 running = false 状态。
- 启动前用
SHOW MASTER STATUS查当前最新 binlog 文件名和 position,填到 instance 的meta.properties或 YAML 配置里 - 若需从历史位置重放,先用
mysqlbinlog --base64-output=decode-rows -v mysql-bin.000001确认该文件是否还存在且可读 - 阿里云 RDS 默认保留 7 天 binlog,自建库建议设
expire_logs_days = 14防断链
row 格式 binlog 是硬性前提,statement 或 mixed 模式会导致数据丢失
Canal 解析的是行级变更事件。如果 MySQL 的 binlog-format 是 statement,像 UPDATE t SET c = c + 1 WHERE id = 1 这类语句只会记录 SQL 文本,Canal 无法还原出新旧行数据;mixed 模式下部分语句仍走 statement,同样不可靠。
- 必须在 my.cnf 中显式设置
binlog-format = ROW,并重启 MySQL(仅 reload 不生效) - 验证方式:执行一条 UPDATE 后,查
SHOW BINLOG EVENTS IN 'mysql-bin.000001' LIMIT 10,确认事件类型是Write_rows_v2/Update_rows_v2/Delete_rows_v2 - 某些老版本 MySQL(5.6 以前)的 ROW 日志不包含完整列值,需升级或打补丁
TCP 直连模式 vs MQ 模式:选错会影响下游消费可靠性
TCP 模式下,Canal Server 直接把事件推给客户端,一旦客户端宕机或网络抖动,未 ACK 的事件就丢了;MQ 模式(Kafka/RocketMQ)靠消息队列的持久化和重试机制兜底,适合生产环境。
- 开发调试可用 TCP 模式快速验证,但上线必须切 MQ 模式
- 用 Kafka 时,确保 Canal 的
kafka.bootstrap.servers配置指向真实集群,且 topic 已手动创建(Canal 不自动建 topic) - Consumer group 名称不能复用——同一 group 多个实例会分摊消费,导致漏事件;每个下游服务应配独立 group.id
权限不足时常见报错及修复动作
Canal 连接 MySQL 报 Access denied; you need (at least one of) the SUPER, REPLICATION CLIENT privilege(s) 或 Failed to dump table structure,基本都是账号权限没给够。
- 必须授予
REPLICATION SLAVE(必需)和REPLICATION CLIENT(必需),SELECT(用于首次表结构拉取) - 执行:
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%'; FLUSH PRIVILEGES; - 注意:RDS 用户可能需通过控制台单独开通「Binlog Dump 权限」,命令行 GRANT 无效
真正容易被忽略的,是 Canal 的 eventStore 内存缓冲区大小与下游消费速度的匹配。默认 buffer.size = 16384,如果 Kafka 生产者吞吐跟不上,buffer 满了会丢弃老事件——这不是 bug,是设计如此。调大 buffer 或加 consumer 实例前,先确认你的消息序列化逻辑有没有隐式阻塞(比如 JSON 序列化里调了 DB 查询)。











