已采纳
回答正文
回复
讨论正文
该问题的正文与回答。
【背景】
订单履约链路:业务服务(K8s,4 副本)→ Kafka 3.5(3 broker,KRaft,acks=all,min.insync.replicas=2)→ 消费端写 MySQL 8.0 业务表。生产端配置:enable.idempotence=true、linger.ms=5、batch.size=256KB、max.in.flight.requests.per.connection 被幂等钳制为 5。我们一直默认「开了幂等 = broker 保证不重不丢」,下游消费端只做了 offset 管理,没有业务级去重(唯一索引只建在 order_no 上,但业务上同一 order_no 本来就只允许一条)。
【现象(带数据)】 过去 6 周,消费端业务表出现 3 次「同一业务事件落了两条」,逐条复盘根因完全一致,且都不是消费端重复消费:
sequence number 从 0 重新计数 → broker 端幂等去重完全不认识这批消息,旧进程迟到的重发 batch 被当新消息正常写入;order_no 唯一索引第二次 INSERT 报 Duplicate entry,我们当时的代码是直接吞异常(当作已存在跳过),但有两条记录的业务字段不同(一条是旧快照),对账时才发现。【已尝试的排查与结果】
__transaction_state 无关(我们没开事务,纯幂等)——幂等的去重窗口是 (PID, partition) 维度的,PID 一变,之前所有去重记录对「新身份」无效,这是设计使然;send() 失败后不自动重试(retries=0),改为进本地 outbox 表重投——重复率降了,但引入了新的「消息滞留 outbox」问题,且发布窗口内 outbox 与内存中已发出未 ack 的 batch 之间仍有缝;INSERT ... ON CONFLICT DO NOTHING 会把合法更新也吞掉。【期望得到的答案形态】 想收集大家生产环境的真实做法,最好带配置或 SQL:
event_id(雪花/UUID)进消息体 + 消费端 (order_no, event_id) 去重表,是不是标准姿势?event_id 的生成时机(业务写事务内 vs send 前)对一致性有什么影响?retries=0 + outbox」但 outbox 重投时又因为 PID 变化重复投递的坑?outbox 与幂等的边界怎么切?【相关站内内容】
max.in.flight 钳制到 5 的机制(id=122)——印证了幂等只覆盖「单 PID + 单分区」的窗口;