canal必须连从库而非主库,因主库dump线程资源紧张且易超时;从库开启log_slave_updates=on后提供稳定binlog供消费,且需配置唯一slaveid、正确权限与位点管理。

Canal 为什么必须连从库而不是主库的 Binlog
Canal 本身不直接读取 MySQL 主库的 Binlog,它依赖的是 MySQL 的 SHOW BINLOG EVENTS 或 COM_BINLOG_DUMP 协议来拉取日志事件。但主库通常不允许 Canal 这类外部订阅服务长期占用 dump 线程——尤其在高并发写入时,dump 线程争抢会拖慢主库复制延迟甚至触发超时断连。
更关键的是:Canal 需要稳定、低干扰的日志流,而从库的 relay log 已经被解析过一次,Binlog 格式(ROW)和位置点都已就位;且从库可单独配置 log_slave_updates=ON,让 relay log 再次落盘为本地 Binlog,供 Canal 安全消费。
- 主库开启
binlog_format=ROW是前提,但从库也必须显式开启log_slave_updates=ON,否则从库没有自己的 Binlog 文件,Canal 连上去会报错Could not find first log file name in binary log index file - 从库建议关闭
skip_slave_start,避免重启后复制中断导致 Canal 拉到空日志 - Canal server 配置里的
slaveId必须全局唯一,如果多个 Canal 实例连同一从库,slaveId冲突会导致部分连接被主库(或从库)主动踢出
从库 Binlog 开启后 Canal 连不上常见报错与修复
最典型的是 Canal 启动时报 java.io.IOException: ErrorPacket [errorNumber=1236, fieldCount=-1, message=Client requested master to start replication from position... —— 表面是位点错,实际常因从库 Binlog 不连续或 Canal 记录的 binlogPosition 超出当前可用范围。
- 先确认从库是否真有 Binlog:
mysql -e "SHOW BINARY LOGS;",若为空,检查log_slave_updates是否生效(需重启 mysqld 或执行SET GLOBAL log_slave_updates=ON;并确认持久化到配置文件) - Canal 的
instance.properties中canal.instance.master.address必须指向从库 IP+端口,不是主库;且该从库账号需有SUPER或REPLICATION SLAVE权限 - 如果从库曾执行过
RESET SLAVE,relay log 被清空,但 Canal 还在尝试读旧位置,需手动重置 Canal 的消费位点:删掉meta.dat或通过curl调用/stop+/start接口触发重新定位
Canal 订阅 DDL 和大事务的兼容性陷阱
Canal 默认只解析 INSERT/UPDATE/DELETE,DDL(如 ALTER TABLE)默认被跳过,除非显式开启。而大事务(比如单个 UPDATE 影响百万行)会拆成多个 Binlog event,但 Canal 会按事务粒度聚合发送,下游若没做好幂等或顺序处理,容易重复或乱序。
- 启用 DDL 订阅需在
instance.properties加:canal.instance.filter.regex=.*\..*(保证正则覆盖 DDL),并设canal.instance.get.ddl=true - 大事务场景下,
canal.instance.transaction.size默认 1024,建议调小(如 100),避免单次推送数据包过大触发 Kafka 拦截或下游 OOM - MySQL 8.0+ 若用了
binlog_row_image=MINIMAL,Canal 可能拿不到完整前镜像(before image),影响 UPDATE/DELETE 的字段级比对——生产环境务必设为FULL
下游消费时如何识别 Binlog 来源是从库而非主库
Canal 自身不透传“这条日志来自哪台机器”,但你可以通过 Entry 结构里的 header 字段间接判断。关键是看 header.sourceAddress 和 header.logfileName 是否匹配你配置的从库地址和 Binlog 前缀。
- 在 Canal client 解析逻辑中,打印
entry.getHeader().getSourceAddress(),应与canal.instance.master.address一致 -
entry.getHeader().getLogfileName()返回类似mysql-bin.000005,这个文件名必须能在从库上用SHOW BINLOG EVENTS IN 'mysql-bin.000005' LIMIT 1查到,否则说明 Canal 实际连的是别的实例 - 如果下游做了多源合并(比如主+多个从),建议在 Canal instance 配置里加
canal.instance.name=slave-shanghai,再把该 name 注入到每条消息的扩展字段中,比依赖网络地址更可靠
真正麻烦的不是连不上,而是连上了却不知道拉到的日志是不是你想要的那个从库的——尤其是当运维临时切了复制链路、或者用了 MGR 多主切换后,Binlog 文件名和位点会突变,这时候靠人工核对 sourceAddress 和 logfileName 是最直接的兜底手段。










