尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Canal 的 ZooKeeper 节点数据损坏了,该如何恢复?

Canal 的 ZooKeeper 节点数据损坏了,该如何恢复? 1. Canal 的 ZooKeeper 节点数据损坏了先别急着重装Canal 的 ZooKeeper 节点数据损坏指的是/otter/canal/destinations/{instance}下的 znode 被误删、内容被写乱或者 ZK 事务日志异常导致节点数据读不出来。表现出来就是 Canal Server 进程还在但 instance 起不来日志里刷KeeperException.NoNodeException或者同步任务卡在某个位点不动。这套东西适合正在用 Canal 做 MySQL binlog 订阅、又用 ZooKeeper 做集群元数据存储的运维和开发同学。我先把结论放前面ZK 里的 Canal 元数据不是不可再生资源绝大多数情况下都能从 Canal 内存、Canal 日志、MySQL binlog 三个地方把位点找回来。真正危险的不是节点丢了而是你随手用set命令往/cursor里塞了一个错误的位点导致下游数据重复或者直接跳过一段 binlog。Canal 在集群模式下默认用ZooKeeperMetaManager管理元数据核心就三类信息消费位点 cursor、过滤规则 filter、客户端标识 client。ZK 在这里的角色像一个公共公告板每个 instance 有自己的区域Active Server 抢到/running这个临时节点后才往里写进度。公告板上的纸条被撕了Server 就不知道从哪继续。下面按“先诊断、再定位、后重建、最后验证”的顺序走一遍命令都可以直接复制。2. 前置准备确认 ZK 健康与 Canal 版本动手恢复之前必须先确认一件事ZK 集群本身是好的坏的只是 Canal 相关的那几个 znode。如果 ZK 集群自己脑裂或者磁盘挂了那要先修 ZK别在应用层瞎折腾。2.1 用四字命令看 ZK 状态ZooKeeper 提供了一组四字命令最常用的是ruok、stat、mntr、cons。它们通过 2181 端口直接发不需要 zkCli。# 探活正常返回 imok echo ruok | nc zk1 2181 # 看角色和连接数确认谁是 leader 谁是 follower echo stat | nc zk1 2181 # 看关键指标延迟、znode 数量、watch 数量 echo mntr | nc zk1 2181 | grep -E zk_avg_latency|zk_znode_count|zk_watch_count|zk_server_state # 看当前所有客户端连接确认 Canal Server 有没有连上 echo cons | nc zk1 2181mntr里重点看zk_znode_count如果这个数比平时骤降说明确实有节点被删了。cons里如果看不到 Canal Server 的 IP说明 Canal 根本没连上 ZK那问题可能出在网络或 ACL不是节点损坏。2.2 确认 Canal 版本和 MetaManager 实现不同版本的 Canal 对 ZK 的重试逻辑不一样。1.1.7 之前对短暂网络抖动容忍度低1.1.8 优化了重试。先确认版本# 看 Canal 安装目录下的版本 cat $CANAL_HOME/conf/canal.properties | grep -i canal.instance.global.mode # 集群模式下应该是 zookeeper如果canal.instance.global.modezookeeper说明用的是 ZK 元数据。如果是spring或memory那 ZK 里的节点损坏跟你的 instance 启动没关系别搞错方向。注意单机模式下 Canal 用meta.dat本地文件存位点改 ZK 是无效的。先确认模式再动手。3. 可复制配置定位损坏节点并重建 znode这一步是核心。先看清楚 ZK 里 Canal 的目录结构长什么样再决定从哪恢复。3.1 用 zkCli 检查节点结构# 连接 ZK 集群 zkCli.sh -server zk1:2181,zk2:2181,zk3:2181 # 列出所有 instance ls /otter/canal/destinations # 假设 instance 叫 product_sync看它的子节点 ls /otter/canal/destinations/product_sync # 正常应该有running cluster cursor filter client_xxx properties # 如果 cursor 或 cluster 不见了就是损坏点逐个 get 看内容get /otter/canal/destinations/product_sync/running # 期望{active:192.168.1.101:11111,ip:192.168.1.101,port:11111} get /otter/canal/destinations/product_sync/cursor # 期望{journalName:mysql-bin.000015,position:456789,timestamp:1713823200000,gtid:,serverId:1} get /otter/canal/destinations/product_sync/cluster # 期望[192.168.1.101:11111,192.168.1.102:11111]如果get报NoNodeException说明节点丢了。如果返回的内容是乱码或者 JSON 解析不了说明数据错乱。3.2 策略一从 Canal 内存 dump 回写首选只要 Active Server 进程没重启它内存里还留着最新位点。Canal 提供了一个dump管理命令强制把内存位点写回 ZK。# 找到 Active Server 的 IP 和端口从 running 节点或进程看 jps | grep CanalLauncher # 发送 dump 命令 echo dump | nc 192.168.1.101 11111执行后去 Canal 日志确认tail -f $CANAL_HOME/logs/canal/canal.log | grep -i write cursor # 期望看到ZookeeperMetaManager write cursor to zk successfully这个方案最快但前提是 Active Server 还活着。如果 Server 已经重启内存位点丢了走策略二。3.3 策略二从 Canal 日志提取位点重建Canal 每消费一批事件都会在 instance 日志里打印位点。日志路径是logs/{instance_name}/{instance_name}.log。# 从日志尾部往前找最新的位点 tail -n 2000 $CANAL_HOME/logs/product_sync/product_sync.log | grep -E cursor:\[ | tail -5 # 典型输出 # pipelineId:1 cursor:[mysql-bin.000015,456789,1713823200000,]拿到位点后用 zkCli 手动重建节点。注意顺序先建父节点再建子节点。# 在 zkCli 里执行 create /otter/canal/destinations/product_sync create /otter/canal/destinations/product_sync/cluster [192.168.1.101:11111,192.168.1.102:11111] create /otter/canal/destinations/product_sync/cursor {journalName:mysql-bin.000015,position:456789,timestamp:1713823200000,gtid:,serverId:1} create /otter/canal/destinations/product_sync/filter .*\\..*serverId必须和 MySQL 主库的server_id一致否则 Canal 拉 binlog 会报错。filter填你原来的订阅规则不确定就填.*\\..*表示全库全表。3.4 策略三从 MySQL binlog 推断兜底位点如果 Canal 日志也没了只能从 MySQL 本身推断。这是最不精确但最安全的方法代价是可能重放一部分数据。-- 在 MySQL 主库执行看当前 binlog 位置 SHOW MASTER STATUS; -- 输出示例 -- File: mysql-bin.000020 Position: 123456 -- 看上一个 binlog 文件的末尾选一个保守起点 SHOW BINLOG EVENTS IN mysql-bin.000019 LIMIT 1 OFFSET 999999999;选一个比当前位点更早的位置比如上一个文件的末尾然后按策略二的方式写进/cursor。下游系统必须支持幂等比如 ES 用主键 Upsert否则会重复。4. 验证请求确认 Canal 恢复拉取 binlog节点重建完不代表完事必须验证 Canal 真的能重新拉 binlog 并同步。4.1 重启 Canal Server 并观察日志# 重启所有 Canal Server $CANAL_HOME/bin/stop.sh $CANAL_HOME/bin/startup.sh # 实时看 instance 日志 tail -f $CANAL_HOME/logs/product_sync/product_sync.log正常启动的日志顺序应该是加载 instance 配置 → 连接 ZK → 读取 cursor → 连接 MySQL → 开始拉 binlog。看到类似下面的行就说明通了start successful..... subscribe successfully, clientId10014.2 用 Canal 客户端或 admin 端口验证位点推进# 通过 admin 端口看 instance 状态 echo list | nc 192.168.1.101 11112 # 看具体 instance 的位点 echo cursor product_sync | nc 192.168.1.101 11112如果位点数字在持续增长说明 binlog 在正常拉取。再往 MySQL 写一条测试数据看下游 ES 或 ClickHouse 有没有同步过去。4.3 检查 ZK 节点是否被正常刷新# 回到 zkCli get /otter/canal/destinations/product_sync/cursor # 位点应该比刚才重建时更新了说明 Canal 在正常回写如果位点一直不动说明 Canal 卡住了去日志里找NoNodeException或ConnectionLossException。5. 本篇常见错排查恢复过程中最容易踩的坑我列几个高频的。报KeeperException.NoNodeException但节点明明存在多半是 ACL 问题。Canal 用的 ZK 账号没有读权限。用getAcl /otter/canal/destinations/product_sync看权限必要时用超级用户重新授权。重建后 Canal 启动报serverId mismatch/cursor里的serverId和 MySQL 的server_id不一致。去 MySQL 执行SHOW VARIABLES LIKE server_id把值改成一致。位点写进去但 Canal 不消费检查/filter节点。如果 filter 是空字符串或者格式不对Canal 会认为没有表需要订阅自然不拉数据。filter 格式是库名.表名多个用逗号分隔全匹配用.*\\..*。ZK 里节点建了但重启后又被删/running是临时节点Server 断开后 ZK 会自动清理这是正常的。但/cursor、/cluster、/filter是持久节点不该被自动删。如果持久节点也消失检查是不是有人配了错误的 cleanup 策略或者 ZK 的autopurge配置误伤。Canal 日志刷ConnectionLossException但 ZK 是好的看 Canal Server 到 ZK 的网络和防火墙以及 ZK 的maxClientCnxns是不是被打满了。echo cons | nc zk1 2181 | wc -l看连接数。提示恢复操作前先对 ZK 做一次快照备份。ZK 的dataDir下snapshot.*和log.*文件复制一份出问题还能回滚。6. 恢复之后把元数据备份和监控补上节点恢复只是救火真正省心的是提前把备份和监控做起来。Canal 的 ZK 元数据量很小定期导出成本极低。#!/bin/bash # backup_canal_zk.sh INSTANCEproduct_sync ZK_HOSTzk1:2181 DATE$(date %Y%m%d_%H%M%S) BACKUP_DIR/backup/canal_zk/$DATE mkdir -p $BACKUP_DIR for node in cursor filter cluster; do zkCli.sh -server $ZK_HOST get /otter/canal/destinations/$INSTANCE/$node $BACKUP_DIR/$node.txt 21 done echo backup done: $BACKUP_DIR配合 crontab 每小时跑一次保留最近 48 小时。监控方面重点盯三个指标Canal 到 ZK 的连接状态、canal_instance_delay消费延迟、/cursor节点是否存在。任何一个异常就告警。如果你在恢复过程中需要反复验证模型对 Canal 配置和 ZK 命令的理解可以用 TaoToken 模型对话 快速核对命令参数接入相关的 API Key 在 API Keys 管理页 获取接入文档在 TaoToken 接入文档。如果是长期跑 Canal 集群运维、需要把排障脚本和 Agent 结合起来的场景Coding Plan 更适合做持续性的编码和自动化任务。最后提醒一句往/cursor写位点之前一定先确认下游系统的幂等能力。位点写早了会丢数据写晚了会重复两者都比节点丢失更难收拾。
返回列表