答案是Canal客户端连接被拒绝,因canal-server未启动或客户端配置的IP/端口错误;需确认server已运行、监听11111端口、防火墙放行,并将application.yml中canalServerHost改为实际可访问IP。

Canal 启动失败报 com.alibaba.otter.canal.protocol.exception.CanalClientException: java.net.ConnectException: Connection refused
这是最常见问题,本质是 Canal Server(即 canal-server)没起来,或客户端连错了地址。不是 MySQL 配置问题,先别急着改 binlog。
-
canal-server必须先启动,且监听端口(默认11111)未被占用:用netstat -tuln | grep 11111确认 - 检查
conf/canal.properties中canal.serverMode是否为tcp(跨机房双写必须用 TCP 模式,kafka模式不适用直连场景) - 客户端连接的 IP 和 port 要和
canal-server实际绑定地址一致;跨机房时不能写localhost或127.0.0.1,得填对方机房可路由的内网 IP - 防火墙/安全组必须放行
11111端口——这点在跨机房场景下极易被忽略
MySQL 主从配置与 Canal 的 binlog 格式强耦合
Canal 依赖 MySQL 的 binlog event 解析,格式不对直接无法消费。不是“开了 binlog 就行”,而是必须满足三要素。
- MySQL 必须开启
binlog,且binlog_format = ROW(STATEMENT或MIXED会导致 Canal 解析失败或丢数据) - MySQL 用户需有
SELECT,REPLICATION SLAVE,REPLICATION CLIENT权限——仅REPLICATION SLAVE不够,Canal 初始化时会查表结构 - 主库
server_id必须唯一,且 Canal 实例的conf/example/instance.properties中canal.instance.mysql.slaveId不能与任何 MySQL 节点冲突(建议设为大于 1000 的值)
双写场景下如何避免循环同步(A→B→A)
两个机房 MySQL 互为上游,Canal 在两边都订阅对方 binlog,若不做隔离,一条 UPDATE 会无限反弹。这不是 Canal 的 bug,是架构设计责任。
- 必须在 Canal instance 层过滤掉“由本机房写入产生的 binlog”:通过
canal.instance.filter.regex配合业务标识字段(如region_flag)或表前缀(如shenzhen_.*)实现逻辑隔离 - 更稳妥的做法是在写入目标库前加轻量判断:比如 Canal 客户端收到 event 后,检查
entry.getHeader().getProperties()中是否含自定义标记(可通过canal.instance.global.spring.xml扩展注入),有则跳过 - 绝对不要依赖 MySQL 自身的
replicate-ignore-db,Canal 是独立客户端,不走 MySQL 复制通道
增量迁移中 canal.instance.tsdb.enable 开关影响断点续传可靠性
跨机房链路不稳定,网络抖动、服务重启频繁,能否准确续传就看 TSDB(时间戳数据库)是否启用。
- 务必设为
true,否则 Canal 重启后从最新 position 拉取,丢失中间 event(尤其在迁移窗口期长、延迟高时) - TSDB 默认用 H2 文件数据库,生产环境建议改为 MySQL:修改
canal.instance.tsdb.spring.xml,替换数据源,并确保canal_instance表存在(脚本在conf/tsdb/下) - 注意
canal.instance.tsdb.url连接的是存储位点的库,不是业务库;该库应部署在本地机房,避免跨机房访问 TSDB 带来单点延迟风险


















