news 2026/10/24 4:54:27

canal高可用部署实战:基于ZooKeeper的MySQL增量同步HA方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
canal高可用部署实战:基于ZooKeeper的MySQL增量同步HA方案

canal要做高可用,真不只是多起两个进程那么简单。很多团队把canal部署成单机,数据同步链路一旦断掉,靠人肉盯日志、手动重启,哪天半夜出了故障,才意识到问题有多被动。这篇我来完整拆解一套Linux环境下canal HA的安装配置方案,从原理到落地一步步说清楚,尽量把我在实际部署中踩过的坑也带出来。

这套方案适合谁?如果你正在做MySQL到消息队列、搜索引擎、异构数据库的增量同步,或者任何依赖binlog变更捕获的场景,只要同步链路的SLA有要求,就可以直接参考。我会以两台canal server + 一套ZooKeeper集群为例,讲清楚高可用选主、位点续传、故障切换这些核心机制,并给出完整的配置文件。

1. 为什么canal HA是数据同步链路的刚需

先聊一个实际痛点。很多团队的canal部署模式是“一台机器跑一个instance”,数据同步链路如下图这种形态:MySQL主库产生binlog,canal伪装成从库拉取,再把解析后的增量数据推给下游。这台canal一旦出问题,下游收不到数据,整个消费链路全部阻塞。

单机的麻烦在于几个短板上线后就会被放大。第一,进程挂掉后依赖人工发现、人工重启,夜间故障的响应时间基本按小时算;第二,canal默认把消费位点放在内存或单机文件里,机器损坏或者目录被误删,位点丢了就只能从头拉,下游要承受重复数据甚至是数据错位;第三,日常升级、换配置、调整JVM参数,都意味着停机窗口。业务方每次听到“要重启canal”都会问一句:影响多久?这个问题的答案往往是“看在不在夜里”。

HA模式解决的就是这些问题。多部署一个canal实例,通过ZooKeeper做分布式协调,同一时刻只让一个实例服务,另一个实时待命。服务实例故障后,备机自动顶上,并且从ZooKeeper中读取上次的消费位点继续拉取增量数据。整个过程业务侧能感知到的只是几十秒的数据延迟,链路不会断。

这套机制对下游消费端是透明的。消费端连接的是canal集群,不必关心当前哪个节点在服务。这里有一个前提要提前讲清楚:HA依赖ZooKeeper选主和位点持久化,也就意味着你必须先搭好一套ZooKeeper集群,再把canal切到ZooKeeper模式下运行。如果只是两台canal放在那里,自己读写自己的文件,那不叫HA,叫“双份麻烦”。

另外提醒一句,canal HA不改变数据语义。canal在发生故障切换时会从持久化位点继续消费,位点之后的数据可能重复投递给下游,消费端最好做幂等处理。Kafka、RocketMQ这类消息队列本身支持幂等键,ES、MySQL这类存储则建议用唯一键去重。

2. 一张图看懂canal HA的工作原理:选主、接管、续传

很多人装完HA能跑通,但对它背后的逻辑一头雾水,出故障时判断就非常被动。我把canal HA拆成三个核心环节,逐个展开。

2.1 canal怎么“伪装”成MySQL从库

canal之所以能拿到MySQL的增量数据,是因为它模拟了一个MySQL Slave实例的交互过程。它向MySQL主库发送dump协议,告诉主库“我是一个从库,把binlog从某个位点开始发给我”。主库收到了这个请求,就会持续把binlog二进制日志推送过来。

这里面有两个技术前提,需要提前确认。第一,MySQL主库必须开启binlog,并且binlog_format必须为ROW。因为只有ROW模式下,binlog中才包含了每一行数据的完整变更内容,canal才能解析出增删改的具体记录。第二,用于连接MySQL的账号必须具备REPLICATION SLAVE和REPLICATION CLIENT权限,否则dump阶段直接报权限错误。这两条我在后面环境准备部分会给出具体配置。

canal把收到的binlog解析成结构化增量事件后,再以TCP或者消息队列的方式推送给消费端。解析过程包括了事务边界识别、表结构映射、字段类型转换,这也是canal相比直接读binlog日志更省心的原因。

2.2 ZooKeeper在canal HA里的三个用处

ZooKeeper在整套方案里承担了三件事。第一件事是选主。每个canal实例启动后会往ZooKeeper的某个节点下注册自己,并尝试创建一个临时节点,谁创建成功谁就是当前服务的active节点。第二件事是监听。standby实例会监听active节点,一旦发现临时节点消失,立刻重新发起创建,谁先创建成功谁接管。第三件事是保存位点。在HA模式下,canal每次处理完一批binlog,会把解析到的位置信息写入ZooKeeper,这样active挂掉之后,新的active能够从上次的位置继续拉取。

ZooKeeper临时节点有一个特性,就是会话超时自动删除。active实例和ZooKeeper之间有心跳机制,如果active实例宕机、断网或者JVM假死,都会导致会话超时,ZooKeeper就会把这个临时节点删掉。这正是故障切换的触发信号。

这里要提一个实际心得:ZooKeeper集群最好独立部署,不要和canal混在一台机器上。虽然混部也能跑,但zk集群本身如果节点挂掉过多导致选不出leader,canal的HA机制就会失效。生产环境建议zk至少三节点,且单独规划机器。如果机器紧张,至少也要让zk的存储目录落在独立的数据盘上,别和系统日志抢IO。

2.3 一次完整的故障切换过程

假设两台canal实例分别部署在两台Linux服务器上,一台是active,另一台是standby。它们的destination名称一致,连接到同一个ZooKeeper集群。正常情况下,active实例在ZooKeeper里持有一个running临时节点,standby实例一直watch这个节点。

当active实例宕机后,会发生下面的过程:

  1. active与ZooKeeper之间的心跳中断,等到会话超时阈值,ZooKeeper删除active持有的临时节点。
  2. standby收到节点变化通知,立即发起临时节点创建。
  3. standby创建成功,成为新的active。
  4. standby从ZooKeeper读取持久化的消费位点,建立MySQL dump连接,从这个位点继续拉取binlog。
  5. 新的active将自己注册的节点信息更新,同时对外提供服务。

整个过程的时间成本主要是ZooKeeper会话超时等待,通常配置在几十秒级别。也就是说,故障自动恢复的时间窗口在几十秒内。如果你在配置里把ZooKeeper的会话超时调得很长,切换时间会成比例增长,这一点在故障演练时要心里有数。

这里有一个重点容易被忽略:canal实例本身的进程还活着,但和下游之间的连接断了,或者GC卡死,这种情况ZooKeeper可能短时间内不会判定会话超时,切换就不会触发。所以监控不能只看canal进程在不在,还要监控它和zk的会话、下游消费延迟这些指标。

3. 环境准备:先把MySQL、ZooKeeper、JDK这几个地基打好

开始配置canal之前,环境准备工作量不小。我按我的部署习惯整理了一套顺序,建议按这个顺序来做。

3.1 服务器规划与JDK安装

我拿一套典型的生产配置举例,两个canal节点加三个ZooKeeper节点。表格如下:

主机IP角色关键端口
node110.0.1.11canal + ZooKeeper2181, 11111
node210.0.1.12canal + ZooKeeper2181, 11111
node310.0.1.13ZooKeeper2181
mysql10.0.1.20MySQL主库3306

之前说过生产环境建议zk独立部署,但如果资源有限,像上面这样把zk和canal混部在两台机器上也是可行的,前提是端口不冲突。canal默认端口为11111,zk默认2181,这个组合不冲突。但一定要注意另一个默认端口:canal的admin接口在部分版本会占用8080端口,如果机器上已经有其他web服务,需要调整。

JDK版本建议使用JDK8或JDK11。canal是Java技术栈,依赖JDK运行,安装方式很简单。直接用系统包管理器安装openjdk就可以了。

yum install -y java-1.8.0-openjdk-devel java -version

安装完成后确认JAVA_HOME环境变量。canal的启动脚本bin/startup.sh会读取JAVA_HOME,如果没设置,启动就会报错找不到java命令。建议直接在/etc/profile里写入:

export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk export PATH=$JAVA_HOME/bin:$PATH

然后执行source /etc/profile让配置生效。

3.2 MySQL端开启binlog和创建授权账号

canal拉取增量数据的前提是MySQL主库已经开启了binlog,且格式是ROW。打开MySQL配置文件my.cnf,在[mysqld]段落下加入:

[mysqld] server_id = 1001 log-bin = mysql-bin binlog_format = ROW binlog_row_image = FULL expire_logs_days = 7 max_binlog_size = 256M

这几个参数逐个解释一下。server_id在主从架构里要求全局唯一,canal伪装成从库时,server_id也必须另配一个并且不能和其他从库重复。binlog_format是ROW这件事很重要,STATEMENT格式下binlog里只有SQL语句,canal没法解析出变更了哪些行的数据。binlog_row_image设置为FULL,让binlog记录完整的前后镜像,否则某些场景解析出来的数据不完整。expire_logs_days是binlog保留天数,根据你的同步链路重要程度来定,太短可能canal故障期间binlog已经被清理,恢复时追不回增量。

配置完之后重启MySQL生效。

然后创建一个专门给canal用的账号。注意账号的主机范围,如果canal和MySQL不在同一台机器,不能用localhost,要允许远端连接。

CREATE USER 'canal'@'%' IDENTIFIED BY 'canal123'; GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%'; FLUSH PRIVILEGES;

创建完账号后,用一个简单命令验证一下能不能正常拉取binlog信息:

mysql -h 10.0.1.20 -ucanal -pcanal123 -e "SHOW MASTER STATUS;"

如果能看到类似下面这样的输出,说明binlog已开启,且账号有权限读取:

File Position Binlog_Do_DB Binlog_Ignore_DB mysql-bin.000012 154 null null

另外,如果你的MySQL主库已经开启了GTID模式,建议在canal的instance配置里开启GTID对齐开关。开启GTID后,位点匹配会更精准,HA切换时不容易出现位点回退问题。GTID模式配置如下:

[mysqld] gtid_mode = on enforce_gtid_consistency = true

开启了GTID后,binlog文件里每个事务都会有唯一的GTID标识,后续在canal配置里把canal.instance.gtidon设为true就行。

3.3 ZooKeeper集群部署

ZooKeeper选主和位点持久化都依赖它,必须保证集群本身可靠。这里以三节点为例,生产环境强烈建议至少三个节点,两个节点的zk集群可能出现脑裂时选不出leader的极端情况。

在每一台机器上下载解压ZooKeeper。我这里用zookeeper-3.6.x的部署为例:

tar -zxf apache-zookeeper-3.6.3-bin.tar.gz -C /opt/ cd /opt/apache-zookeeper-3.6.3-bin/conf cp zoo_sample.cfg zoo.cfg

修改zoo.cfg:

tickTime=2000 initLimit=10 syncLimit=5 dataDir=/data/zookeeper clientPort=2181 server.1=10.0.1.11:2888:3888 server.2=10.0.1.12:2888:3888 server.3=10.0.1.13:2888:3888

dataDir建议放到独立的数据盘目录,不建议和系统目录混用。然后每台机器创建dataDir目录,并在其中创建一个myid文件,内容为各自的序号:

mkdir -p /data/zookeeper echo "1" > /data/zookeeper/myid # 第一台机器 echo "2" > /data/zookeeper/myid # 第二台 echo "3" > /data/zookeeper/myid # 第三台

随后在三台机器上依次启动:

cd /opt/apache-zookeeper-3.6.3-bin bin/zkServer.sh start

检查启动状态:

bin/zkServer.sh status

正常的三节点集群,一个节点会显示leader,另外两个显示follower。如果全部显示standalone,说明集群没配好,检查myid和zoo.cfg中的IP列表。

最后测试一下,确保canal节点所在的机器能访问zk集群的2181端口。用下面的命令验证:

yum install -y nc nc -vz 10.0.1.11 2181

4. canal server高可用部署实战:从下载到配置一步步来

环境准备好后,进入正式部署环节。canal的项目发布包分为deployer和adapter两部分,其中deployer是核心服务端,负责拉取binlog并推送给下游;adapter是官方提供的消费适配器,可以直接对接Kafka、RocketMQ、ES、MySQL等。本节先讲deployer,因为HA的核心链路在它这里。

4.1 下载并规划canal目录

当前稳定版本以1.1.x为主流,我以canal.deployer-1.1.6版本为例子,下载方式就是去官方GitHub Releases页找到对应tar.gz包。这里只讲包内结构,具体文件名可以对着release页面认。

mkdir -p /opt/canal tar -zxf canal.deployer-1.1.6.tar.gz -C /opt/canal/ cd /opt/canal

解压后的目录结构如下:

目录/文件作用
bin/startup.sh启动脚本,会自动加载JAVA_HOME
bin/stop.sh停止脚本
conf/canal.propertiescanal server全局配置
conf/example/instance.properties单个instance的数据源配置
conf/spring/*.xml不同模式的Spring装配文件
lib/依赖jar包
logs/运行日志目录

这个包本身是给单机用的,部署成HA时我们需要准备两份,分别放在两台Linux服务器上。我建议直接用同样的路径,避免后续维护时路径不一致带来的混淆。

4.2 canal.properties核心参数配置

canal.properties是全局配置文件,重点改动以下几个参数。

canal.id = 1 canal.ip = 10.0.1.11 canal.port = 11111 canal.register.ip = 10.0.1.11 canal.register.port = 11111 canal.zkServers = 10.0.1.11:2181,10.0.1.12:2181,10.0.1.13:2181 canal.instance.global.spring.xml = classpath:spring/zk-instance.xml canal.instance.global.lazy = false canal.destinations = example

这里有几个点,一定要讲透。

第一,canal.ip和canal.register.ip的区别。canal.ip是服务本身监听的地址,如果机器有多块网卡,需要显式指定,避免canal把内网和公网地址搞混。canal.register.ip是向所在集群注册节点时对外暴露的地址,消费端会通过这个IP连接canal。两台canal机器的register.ip分别填自己机器的内网IP。

第二,canal.zkServers是整套HA的命脉。这里写的是zk集群的地址列表,canal实例启动后会连接这些地址,完成节点注册和位点持久化。如果不配置这一项,canal会退化为单机模式,HA无从谈起。

第三,canal.instance.global.spring.xml是HA切换的关键开关。默认值通常是classpath:spring/memory-instance.xml,这种模式下canal把元数据和位点存内存,进程重启后位点丢失。要实现HA,必须改成classpath:spring/zk-instance.xml,让canal把位点和元数据持久化到ZooKeeper中。改完这个配置后,canal启动时才会去注册zk节点并创建持久化位点。

第四,canal.destinations是instance名称列表,多个instance可以用逗号分隔。每台canal机器上只要配置的destination名称一致,它们就自动组成一个HA组。比如我上面写的example,两台机器都要有这个同名destination。

另外有一个参数建议打开,canal.instance.global.lazy=false。这是让canal在启动时就初始化instance并抢占zk节点。如果设为true,实例启动时会延迟初始化,备机可能要等真正发起连接时才去抢占节点,不利于自动切换。

4.3 instance.properties数据源与位点配置

接下来进入每台机器上的conf/example/instance.properties,这是单个数据同步任务的核心配置。以我用的MySQL主库10.0.1.20为例:

canal.instance.mysql.slaveId = 654321 canal.instance.master.address = 10.0.1.20:3306 canal.instance.dbUsername = canal canal.instance.dbPassword = canal123 canal.instance.connectionCharset = UTF-8 canal.instance.filter.regex = .*\\\\.testdb..* canal.instance.gtidon = true

逐个来看。

canal.instance.mysql.slaveId注意两点。第一,不能和MySQL真实从库的server_id冲突;第二,两台canal机器的这个值不能相同。因为canal伪装成MySQL的一个从库,这个ID在MySQL视角里必须是一个唯一的从库标识。如果两台canal配了同一个slaveId,MySQL会认为是同一个从库在重复连接,可能出现连接互相踢掉的问题。

canal.instance.master.address是MySQL主库地址和端口。注意这里填的是主库地址。虽然canal HA部署在多个节点,但所有canal节点拉的是同一个主库的binlog。

canal.instance.filter.regex是过滤规则,格式为正则表达式。上面的写法表示只监听testdb库所有表。实际生产环境,如果你只想同步某几张表,可以更精确地写成:

canal.instance.filter.regex = testdb\\.(t_user|t_order)

这里有个容易踩坑的细节:在properties文件里写正则,反斜杠需要双写。因为properties文件本身会做一层转义解析,Java字符串又会做一层,最后正则引擎收到的才是真正的反斜杠。所以你看上面的写法是四个反斜杠对应一个点号,四个反斜杠在properties解析后变成两个,Java字符串再解析后变成一个,这样正则的.才是匹配点号。

canal.instance.gtidon设为true,表示连接MySQL时使用GTID作为位点标识。前提是MySQL主库已经开启了GTID模式。这个开关配合zk-instance.xml,切换后的位点恢复准确度更高。

配置完毕之后,再把这份instance.properties文件复制到另一台canal服务器的相同路径下。记住要把slaveId改掉,把master.address保持同一个主库地址,以及把过滤规则检查一遍。十次有九次问题出在“两台机器配置不一致”上。

4.4 启动canal并观察节点注册情况

在两台Linux机器上分别启动canal:

cd /opt/canal sh bin/startup.sh

启动后立刻看日志:

tail -f logs/canal/canal.log tail -f logs/example/example.log

正常情况下,一台机器的日志里会出现类似“register to zk”的字样,另一台机器则会等待并注册为备机。canal.log里如果出现“canal is running with [destination = example]”这样的信息,说明该实例成功启动。

再去ZooKeeper里确认节点状态。进入一台zk机器执行:

bin/zkCli.sh -server 10.0.1.11:2181 ls /otter/canal/destinations/example

应该能看到类似cluster这样的节点,再往下看running节点的内容:

get /otter/canal/destinations/example/running

能读到当前active节点的IP,就说明选主成功。再用:

ls /otter/canal/destinations/example/cluster

能看到两台机器的地址都在,说明它们已经组成一个HA组。

启动时还要检查端口是否被占用。canal的netty服务默认监听11111端口,如果看到端口被占用,一般有两种可能:一是另一个canal实例已经起来了,二是之前有残留的java进程没有清理干净。用netstat排查:

netstat -tlnp | grep 11111

确认监听地址是0.0.0.0还是具体IP,如果只想让本机访问,可以不用管;如果要让远端消费端连接,需要确保安全组或者防火墙放行该端口。

5. 消费端如何对接canal高可用集群

canal server的HA实现了,消费端也要跟上。不少人在这一步踩坑,认为server高可用之后,消费端什么都不用改,结果故障切换后消费端连不上,整个链路还是断的。

5.1 TCP模式下消费端的ZooKeeper感知能力

canal提供了两种主要消费方式,一种是原生TCP客户端,一种是MQ消息队列。TCP模式下,消费端需要主动获取当前active实例的地址。canal官方的Java客户端支持通过ZooKeeper自动感知当前master,启动时只需要配置zk地址。

以Java客户端为例,地址字符串是这么写的:

String destination = "example"; CanalConnector connector = CanalConnectors.newSingleConnector( new InetSocketAddress("10.0.1.11", 11111), destination, "", "" );

但这种方式写死了某个canal实例的地址,HA切换后消费端就会连不上。正确做法是使用基于ZooKeeper的连接器:

String zkServers = "10.0.1.11:2181,10.0.1.12:2181,10.0.1.13:2181"; CanalConnector connector = CanalConnectors.newClusterConnector(zkServers, destination, "", "");

newClusterConnector构造方法接收的是zk地址列表,客户端内部会去zk中查找当前active实例并与之建立连接。当active切换时,客户端会感知到running节点的变化,自动重连到新的active节点。这个机制对不熟悉canal的团队来说属于“隐藏知识点”,如果你现在用的是newSingleConnector,务必检查一下。

上面的示例严格来说不够准确的地方在于,CanalConnectors的API不同版本之间方法名有变化,但核心思路都一样:基于zk的cluster connector代替写死的single connector。具体方法名请以你引入的client版本Javadoc为准。

5.2 adapter模式如何跟上HA

如果你用的是官方adapter包,也就是canal.adapter那个组件,它在tcp模式下默认支持通过zk感知master。application.yml里按如下方式配置:

canal.conf: mode: tcp canalServerHost: 10.0.1.11:11111 zookeeperHosts: 10.0.1.11:2181,10.0.1.12:2181,10.0.1.13:2181

这里canalServerHost填写任意一台canal的地址,adapter在启动时如果发现配置了zookeeperHosts,会优先通过zk获取当前active实例,并动态维护连接。当active切换之后,adapter会自动重连新的active。如果你不配置zookeeperHosts,adapter就只会连接写死的canalServerHost,切换后链路必断。

如果你是通过Kafka或RocketMQ的方式消费,那canal server会先把增量数据投递到MQ,消费端只跟MQ打交道,canal本身的HA对消费端完全透明。这种情况下,canal HA只需要保证投递端持续有数据写入MQ即可。

不管哪种消费方式,消费端都要做一件事:幂等。HA切换后,新的active实例是从zk中记录的位点继续拉取,但ZK位点已经记录到哪个binlog位置,取决于canal的flush时机。极端情况下,下游可能收到重复的增量记录。对MQ消费场景,建议在消费逻辑里用业务主键做去重;对写入MySQL/ES的场景,建议用唯一键或版本号保证幂等。

6. 高可用切换演练:亲手把active节点kill掉看结果

配置完成后不要直接上线,先做一次故障演练。这一步能帮你确认HA是否真的生效,也能让你心里有底,知道切换的实际耗时是多少。我按照一个标准的演练流程来演示。

6.1 确认当前active节点

先登录zk确认当前running节点在哪个机器上:

bin/zkCli.sh -server 10.0.1.11:2181 get /otter/canal/destinations/example/running

假设返回的IP是10.0.1.11,说明当前active是node1。再登录node1,检查canal的日志和进程:

cat logs/example/example.log | tail -n 20 ps -ef | grep canal | grep -v grep

OK,node1是active,node2是standby。为了确认数据链路的状态,可以同时往MySQL测试表里插入几行数据,然后看消费端是否收到。

6.2 主动kill掉active节点

直接杀掉node1上的canal进程:

ps -ef | grep canal | grep -v grep | awk '{print $2}' | xargs kill -9

不要用stop.sh,因为stop.sh是优雅停机,会先把节点从zk摘除,这样standby立刻接管,无法验证“进程突然死掉”这个场景。真正要模拟的是进程异常退出,所以用kill -9。

杀掉之后立刻登录node2,观察日志变化:

tail -f logs/example/example.log

同时登录zk,观察running节点的变化:

get /otter/canal/destinations/example/running

正常情况下,几秒到几十秒之后,running节点的IP会变成10.0.1.12。切换的耗时取决于zk会话超时时间,如果你的zk配置没有特调,一般会看到30秒到40秒左右。zk配置中tickTime、sessionTimeout这些参数,会影响这个时间窗口的长短。

node2接管后,立刻往MySQL测试表再插入一条数据,观察消费端是否能继续收到增量数据。如果消费端在30秒延迟后继续正常消费,说明切换完全成功。

6.3 恢复原active节点,验证不会出现双活

node1进程被kill之后,如果我们要把node1重新拉起来,直接执行startup.sh。启动后观察日志,这个节点不会立即抢占active,而是会进入standby状态。因为此时node2已经持有了running临时节点。

重点来了,此时一定要去zk里看running节点,确认它还是node2的IP,而不是被node1抢回去。如果node1启动后把running抢走了,说明配置里存在冲突,大概率是zk-instance.xml没有配置好,或者两个节点的destination名称不一致。正常情况下,不应该出现两个节点同时处于active状态,那就意味着消息被同时消费两次。

检查完原节点恢复为standby后,可以再杀掉node2做反向切换演练。这个步骤建议做一次,因为灾后恢复场景和首次故障场景的细节略有不同,跑通一遍心里才踏实。

6.4 切换指标记录

我给一个切换演练的记录表,大家照着填,评审时直接给这个表就行:

观测项第一次切换恢复后切换
触发方式kill -9 node1kill -9 node2
zk会话超时配置默认约30秒默认约30秒
实际切换耗时约28秒约31秒
消费端感知延迟约31秒约34秒
是否出现重复消费出现1条出现0条
数据是否丢失否否

从表格看到,重复消费问题在第二次演练中没有出现,但这不表示永远不会出现。消费端必须坚持幂等原则,一旦流量高峰时zk写入延迟,极端情况下重复消费概率会上升。

7. 实际部署中绕不开的坑和排查思路

canal HA部署一次就能顺利跑通的情况,我几乎没有遇到过。下面这些坑是高频问题,不少都是我亲手踩过又花时间排查的,大家提前对照。

7.1 MySQL连接报错:账号权限和密码加密方式

启动之后example.log里报类似Communications link failure或者Access denied for user 'canal'@'...',百分之八十是账号权限问题。先去MySQL里执行:

SELECT user, host FROM mysql.user WHERE user='canal';

确认账号的host允许canal所在机器的IP连接。如果用的是MySQL 8.0,还要确认默认认证插件是否为caching_sha2_password。canal老版本对这种加密方式适配有缺陷,建议创建账号时指定mysql_native_password:

CREATE USER 'canal'@'%' IDENTIFIED WITH mysql_native_password BY 'canal123'; GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%'; FLUSH PRIVILEGES;

MySQL 5.7没有这个问题,MySQL 8.0一定要先确认。

7.2 ZooKeeper会话频繁断开

zk连接正常,但canal日志里反复出现Session expired、Connection loss之类的报错,可能的原因有三个。第一是zk集群本身不稳定,三节点之间时钟偏差过大。ZooKeeper对节点间时钟偏差很敏感,建议配置NTP时间同步。第二是zk的jvm堆内存给得太小,GC频繁导致处理请求变慢,导致会话超时。第三是canal节点和zk节点之间的网络有丢包,用ping和nc多测一下。

还有一个容易被忽略的点:不要使用zk的默认dataDir路径,也就是zk解压目录下的data目录。这个目录通常在系统盘,系统日志和临时文件一多,IO压力上去,zk的写性能会波动,直接导致会话超时。把dataDir挪到独立数据盘,是一项低成本高收益的改动。

7.3 位点冲突导致的重复消费或丢数据

HA切换后,如果新active实例拉取位点比旧active实际消费位点要早,下游就会收到重复数据。要确认位点信息,可以去zk里查看实例的位点节点路径。这里提醒一点,位点节点会存放在zk节点下面,路径包含destination名称,切换前最好先确认位点是否有持续更新。

如果发现位点没有更新,检查canal.properties里的instance.global.spring.xml是否真的改成了zk-instance.xml。如果你用默认的memory模式,在zk里能看到节点注册,但看不到位点持久化,切换后新active会从当前binlog最新位置开始拉,这就会丢数据。

位点冲突问题还有一种隐蔽场景:同一套配置跑了多套zk环境。比如测试环境、预发环境的zk地址写混了,两台canal实例分别注册到不同的zk集群,看似都在运行,实际上各拉各的位点。这种问题排查起来最折磨人,配置上线前建议三个人互查一遍zk地址。

7.4 端口冲突和网卡IP识别错误

canal的netty端口是11111,如果你在同一台机器上部署多套canal实例,端口必须错开。在canal.properties里修改canal.port时,要注意自定义端口必须在1024以上,并且要同步修改防火墙规则。

关于canal.ip和register.ip,我再强调一遍。如果Linux机器配置了多块网卡,canal可能会识别出错误的IP,导致消费端连接失败。此时必须在canal.properties里显式指定canal.ip为内网IP,同时让register.ip保持通信可达。

7.5 升级canal版本时的特殊注意

canal版本升级时,官方release包之间的instance配置项可能有差异。升级前先备份conf目录,然后对比新旧canal.properties,把不认识的参数挨个查一遍。不要直接复制旧配置覆盖新配置,有些参数在新版本里改了语义,沿用旧值可能导致启动报错或者行为不一致。

升级顺序上,建议先升级standby节点,确认standby能正常注册并观察到active节点后才升级active节点。这样最坏情况下也不会造成链路长时间中断。

我在实际部署里的体会是,canal HA本身配置量不大,真正的复杂度全在依赖环境和不一致上。MySQL的binlog参数、zk的稳定、两台实例的配置一致性,任何一个环节没对齐,切换时都会露馅。配置完成后一定要做一次完整的故障演练,不要等线上出了故障才开始研究zk会话怎么排查。

最后分享一个小技巧:每隔一段时间去zk里看一眼running节点的持有者,结合canal的运行日志做一次快照,记录当前active节点是谁。等到故障发生时,你手里有一张“正常状态基准表”,排查时间能省下一大截。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/24 4:54:11

PMIC+MCU协同实现动态调压与低功耗电源管理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 4:54:08

PMIC加单片机实现可编程电源管理:PCA9422与PIC18F4550动态调压实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 4:54:00

PCA9422+PIC18F4585电源管理系统设计与实战分解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 4:52:58

无界鼠标实战指南:一套键鼠控制多台电脑的原理与配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 4:52:27

深度学习框架源码编译:Compute Capability与NVCC参数排查指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华