事情本身不复杂,线索都在日志里。为了补一个新指标,把订单 topic 的 offset 重置到六小时前重放,消费端逻辑是先查余额再扣减,重放后对账发现 300 多笔订单出现两笔扣款记录。
第一反应是「扣款结果写库没有唯一约束兜底」,验证下来不成立。
排查方向其实有限,先把可能性排一排。消费端没有做幂等,重放等于重新执行业务。另一种情况是offset 重置时没停消费者组,新旧两个实例同时消费。
不容易想到的是,重放用了新的 group id,位点信息完全独立。
第一件事是看消费组位点与成员
kafka-consumer-groups.sh --bootstrap-server kafka:9092 --describe --group order-settle
接着重放前停掉所有消费者实例
kubectl scale deploy order-settle --replicas=0 -n trade
给扣款表加业务单号唯一索引
python -c "print('ALTER TABLE pay ADD UNIQUE KEY uk_biz(biz_no);')"
末了用限速重放并观察重复日志
kafka-consumer-groups.sh --bootstrap-server kafka:9092 --group order-settle --reset-offsets --to-datetime 2026-11-14T02:00:00.000 --execute
改完先别撤,确认这几项。重放完成后同一订单只有一条扣款记录,对账差值为零,重复消费日志计数归零。
重放是运维手段,业务侧必须有幂等兜底,否则任何一次位点调整都会变成资损。报错文案容易误导,真正的原因往往在相邻那一层的日志里。
