最近在自建 PolarDB-X 集群上遇到一个比较有意思的问题。
集群本身已经恢复正常,CDC global binlog 也在正常推进,但是执行下面这个命令时报错:
SHOW SLAVE STATUS;
报错信息如下:
ERROR 9204 (HY000):
[PXC-9204][ERR_REPLICATION_RESULT] {0}
一开始看到 SHOW SLAVE STATUS
报错,很容易联想到 DN 节点的三副本复制是不是异常了。但排查之后发现,问题并不在 DN 三副本,而是 CDC 元数据里残留了旧节点,导致 CN 访问了一个已经失效的 CDC daemon 地址。
这篇文章记录一下完整排查过程。
背景
这套 PolarDB-X 是自建在 Kubernetes 上的,集群组件包括:
CN 2 个
DN 2 组
CDC 2 个
GMS 1 组
之前集群所在物理机发生过故障,恢复过程大致是:
物理机恢复
-> Kubernetes Node 恢复
-> PolarDB-X 各组件恢复
-> CDC 曾经手动删除并重新拉起
后面业务访问和 CDC global binlog 都恢复正常。
执行:
SHOW MASTER STATUS;
可以正常返回当前 global binlog 位点,例如:
binlog.000009 468571716
执行:
SHOW FULL BINARY LOGS;
也能正常看到 binlog 文件列表。
但执行:
SHOW SLAVE STATUS;
却报:
[PXC-9204][ERR_REPLICATION_RESULT]
第一反应:是不是 DN 三副本异常?
因为 MySQL 里 SHOW SLAVE STATUS
通常用于查看复制状态,所以第一反应是:PolarDB-X 的 DN 是三副本,是不是这个命令在查看 DN 的 slave 状态?
后来确认并不是。
PolarDB-X 里的 DN 三副本不是 MySQL 原生主从复制模型,而是底层 consensus paxos 类机制。SHOW SLAVE STATUS
在 PolarDB-X 里主要对应的是 CDC Replica/RPL 模块,也就是 PolarDB-X 作为同步目标时,通过类似下面这些命令建立的复制链路:
CHANGE MASTER TO ...
START SLAVE;
STOP SLAVE;
RESET SLAVE;
SHOW SLAVE STATUS;
所以这个命令查的不是 DN 三副本状态,而是 PolarDB-X CDC Replica/GDN/导入类链路状态。
源码确认
查看 CN 源码可以看到,SHOW SLAVE STATUS
的处理入口在:
LogicalShowSlaveStatusHandler
核心逻辑类似这样:
String daemonEndpoint = CdcTargetUtil.getReplicaDaemonMasterTarget();
res = PooledHttpHelper.doPost(
"http://" + daemonEndpoint + "/replica/showSlaveStatus",
ContentType.APPLICATION_JSON,
JSON.toJSONString(sqlNode.getParams()),
10000
);
也就是说,当客户端执行:
SHOW SLAVE STATUS;
CN 实际上会去找 CDC daemon,然后访问:
/replica/showSlaveStatus
再看 CDC 侧源码,/replica/showSlaveStatus
最终会查询 RPL 元数据,例如:
rpl_state_machine
rpl_task
然后组装类似下面这些字段:
Slave_IO_Running
Slave_SQL_Running
Seconds_Behind_Master
Master_Host
Master_Port
Running_Stage
Channel_Name
这就说明:
SHOW SLAVE STATUS 查的是 CDC Replica/RPL 状态,不是 DN 三副本状态。
直接访问 CDC 接口
既然 CN 是访问 CDC daemon 的 REST 接口,那就直接从 CDC pod 内访问一下:
curl -X POST -d '{}' http://127.0.0.1:3007/replica/showSlaveStatus
返回:
{"code":200,"data":"[]","msg":"success"}
两个 CDC pod 都是这个结果。
这说明:
CDC daemon REST 接口本身正常。 当前没有配置 Replica/RPL 同步任务。 正常情况下 SHOW SLAVE STATUS
应该返回空结果,而不是报错。
继续从 CN pod 里访问当前两个 CDC pod:
curl -X POST -d '{}' http://10.10.157.30:3007/replica/showSlaveStatus
curl -X POST -d '{}' http://10.10.157.124:3007/replica/showSlaveStatus
也都能正常返回:
{"code":200,"data":"[]","msg":"success"}
所以网络本身没有问题,CDC 接口也没有问题。
那为什么 CN 执行 SQL 会报错?
查看 CN 日志
在 CN 日志里找到了对应异常:
SQL: SHOW SLAVE STATUS
ERR-CODE: [PXC-9204][ERR_REPLICATION_RESULT]
同时慢日志里这条 SQL 的耗时是:
rt=10006222
约等于 10 秒。
而源码里 CN 调 CDC 接口的超时时间正好是:
10000
这基本说明:CN 执行 SHOW SLAVE STATUS
时,访问 CDC daemon 超时了。
但我们手动从 CN 访问当前 CDC pod 是正常的。
所以问题很可能是:
CN 查询出来的 CDC daemon endpoint 不是当前真实的 CDC pod。
关键表:binlog_node_info
CN 获取 CDC daemon endpoint 时,会查询 MetaDB 里的 binlog_node_info
表。
重点逻辑类似:
select ip, daemon_port
from binlog_node_info
where cluster_type = 'BINLOG'
and role = 'M';
于是查看这张表:
select cluster_type, role, ip, daemon_port, status, gmt_heartbeat
from binlog_node_info
order by cluster_type, role, ip;
结果发现了问题:
cluster_type role ip daemon_port status gmt_heartbeat
BINLOG M 10.xx.xx.120 3007 0 2026-06-16 07:05:33
BINLOG M 10.xx.xx.124 3007 0 2026-07-01 14:40:58
BINLOG S 10.xx.xx.30 3007 0 2026-07-01 14:40:58
BINLOG S 10.xx.xx.41 3007 0 2026-06-16 07:05:32
当前真实 CDC pod 只有:
10.xx.xx.30
10.xx.xx.124
但是表里还残留了两个旧 IP:
10.xx.xx.120
10.xx.xx.41
其中更严重的是:
10.xx.xx.120 role=M status=0
也就是说,一个已经不存在的旧 CDC 节点,仍然被标记成 master,并且还是可用状态。
从 CN pod 里访问这几个 endpoint:
10.xx.xx:3007 -> timeout
10.xx.xx.124:3007 -> success
10.xx.xx.30:3007 -> success
10.xx.xx.141:3007 -> timeout
问题终于明确了。
根因
这次问题的根因是:
CDC 元数据表 binlog_node_info 中残留了旧 CDC 节点记录。
旧节点已经不可达,但仍然保持 role='M'、status=0。
CN 查询 CDC daemon master 时可能拿到旧 IP,导致访问超时。
最终 SHOW SLAVE STATUS 报 PXC-9204。
这个问题和之前物理机故障恢复过程有关。
推测过程是:
物理机故障
-> 旧 CDC pod 非正常退出
-> 旧 CDC daemon 没有机会优雅下线/更新元数据
-> CDC pod 后续重建,获得新 IP
-> 新 CDC 正常注册
-> 旧 CDC 元数据残留
-> 表里出现两个 BINLOG master
-> CN 访问旧 master 超时
修复方式
修复前先备份 binlog_node_info
:
kubectl exec -n polardbx-operator-system <cn-pod> -c engine -- \
bash usr/bin/ctmeta -N -B -e "select * from binlog_node_info;" \
> root/binlog_node_info.before_fix.tsv
注意这里要用:
bash usr/bin/ctmeta
因为当前容器里的 /usr/bin/ctmeta
是脚本,但缺少 shebang,直接执行会报:
exec format error
然后把旧节点标记为不可用:
update binlog_node_info
set status = 1, role = 'S'
where cluster_type = 'BINLOG'
and ip in ('10.xx.xx.120', '10.xx.xx.41')
and gmt_heartbeat < '2026-06-17 00:00:00';
再次查询确认只剩当前真实 CDC master 可用。
修复后执行:
SHOW SLAVE STATUS;
不再报 PXC-9204
。
因为当前没有 Replica/RPL 任务,所以正常结果应该是空结果。
总结
这次问题最容易误判的地方是 SHOW SLAVE STATUS
这个名字。
在 MySQL 里,它通常意味着主从复制状态;但在 PolarDB-X 中,它更偏向 CDC Replica/RPL 链路状态,并不是 DN 三副本状态。
这次真正的问题不是:
DN 三副本异常
CDC global binlog 异常
Replica 任务异常
而是:
CDC 元数据残留旧节点,导致 CN 选错 CDC daemon endpoint。
排查这类问题时,可以按这个顺序:
1. SHOW MASTER STATUS 看 global binlog 是否正常推进
2. 直接访问 CDC replica/showSlaveStatus 接口
3. 从 CN pod 访问 CDC REST 端口,确认网络
4. 查询 binlog_node_info,确认是否有旧 IP、多个 master、过期 heartbeat
5. 修复旧节点 status/role
这类问题也提醒我们:自建 PolarDB-X 集群在经历物理机故障、K8s 节点异常、Pod 非优雅退出后,除了看 pod 是否 Running,还要检查 CDC/GMS 元数据是否存在脏记录。尤其是 CDC 这种依赖元数据选主和路由的组件,旧节点残留会导致一些看似随机、但本质很明确的异常。
✪ 本文是小编的第209篇文章,第一阶段目标是累计输出 1000 篇优质内容,核心是为了提醒自己:保持学习、保持记录、保持分享,如果觉得本文对你有帮助,欢迎点赞、转发、在看三连!

点个“赞 or 在看” 你最好看!




