canal同步失败时canalclientexception: something goes wrong本质是客户端收不到服务端推送,需依次排查:1.确认canal-server进程运行且日志中有connect to destination成功记录;2.检查canal.properties中canal.register.ip是否误配为127.0.0.1;3.验证mysql的binlog_format=row及binlog_row_image=full是否启用;4.核对canal.instance.master.address端口与实际数据库端口一致。

Canal 同步失败时 com.alibaba.otter.canal.protocol.exception.CanalClientException: something goes wrong 怎么定位
这错误本质是 Canal 客户端收不到服务端推送,不是 MySQL 配置错了就是 Canal Server 没连上。先确认 canal-server 进程在跑,再看它的日志里有没有 connect to destination 成功记录。
常见卡点:
-
canal.properties里canal.register.ip写成了127.0.0.1,导致客户端连的是自己而不是 Canal Server 所在机器 - MySQL 的
binlog_format不是ROW,或没开binlog_row_image=FULL,Canal 解析不了变更事件 - Canal instance 配置里的
canal.instance.master.address端口写错(比如写了3306却连了只开3307的从库)
用 SimpleCanalConnector 拉取数据时,为什么总是重复消费或漏数据
根本原因是没正确管理 batchId 和 ack。Canal 不是消息队列,它不自动 commit offset;每次 getWithoutAck() 返回的 Message 必须显式调用 ack(batchId),否则下次拉取还会拿到这批。
典型误操作:
- 业务处理出错后没
rollback(batchId),而是直接丢弃,导致该 batch 永远卡在未 ack 状态,后续拉取会跳过它(表现就是“漏”) - 多线程并发调用
getWithoutAck(),但共用了同一个 connector 实例,batchId被覆盖,ack 时对不上(表现就是“重复”) - 没检查
message.getId() == -1,就直接解析,其实这是空轮询结果,不是真实 binlog
建议:每个消费线程独占一个 SimpleCanalConnector,且必须包在 try-finally 里确保 ack 或 rollback 执行。
同步到 Elasticsearch 时,CanalEntry.EventType.DELETE 对应的文档删不掉
Canal 发送的是逻辑删除事件,但 ES 删除需要 _id,而 DELETE 类型的 entry 里只有主键字段名和值,没有表名、数据库名这些上下文信息——你得靠 entry.getHeader().getTableName() 和 entry.getEntryType() == EntryType.ROWDATA 先过滤出有效行事件,再从 rowChange.getBeforeColumnsList() 里取主键值。
关键细节:
- MySQL 多主键时,
beforeColumnsList是按定义顺序排列的,不能硬写get(0)取 id - ES 的
delete by id接口要求_id是字符串,而 MySQL 主键可能是long,要显式String.valueOf() - 别在
INSERT/UPDATE分支里做 delete,DELETE 事件是独立 entry,必须单独处理
Canal instance 配置中 canal.instance.filter.regex 不生效
这个正则匹配的是 database.table 格式,不是单表名。比如想只同步 user 库的 order 表,得写 user\.order,而不是 order 或 user.order(点号要转义)。
更隐蔽的问题:
- MySQL 开启了 GTID,但 Canal instance 没配
canal.instance.gtidon=true,会导致 filter 完全不加载(日志里有gtid is not enabled提示) - 正则里用了
^或$锚点,但 Canal 匹配时不做全字符串匹配,只用Matcher.find(),所以不用加 - 多个库表用逗号分隔,末尾多了一个逗号(如
db1\.t1,db2\.t2,),会导致整个 regex 编译失败,instance 启动失败
调试技巧:把 regex 改成 .*\..*,确认能收到所有事件后再逐步收紧,避免一上来就过滤过严看不到日志。
Canal 的坑不在功能多,而在每个环节都依赖上下游状态严格对齐——MySQL 的 binlog 位点、Canal 的 client ack、目标库的幂等写入,三者只要一环松动,增量就会漂移。最稳妥的做法是定期用 checksum 对比源和目标的最新 N 条记录,而不是只盯着 Canal 日志有没有报错。











