退款补偿凌晨两点触发,三台实例的日志里各有一行「开始补偿」,退款流水表同一笔订单出现三条记录,金额合计多退 8600 元。碰到这种情况,我一般先确认改动有没有真的生效。
原因不复杂,补偿任务跟着应用一起部署,每个实例都注册了同一条调度,任务里没有任何互斥。锁如果只加在单条记录上也不管用,扫批次这一步三份都在跑。
「补偿任务重复」这类改动最怕改一半,回滚脚本先准备好,再动生产。别只看报错的最后一行,往上看几行,真正的起因通常在上面。
SET compensation:refund:20260301 1 NX EX 300 # 返回 OK 才继续 否则本轮退出 SELECT order_no, count(*) FROM refund_flow GROUP BY order_no HAVING count(*) > 1;
同类问题如果隔一段时间又出现,多半是某个定时任务或重启流程把改动冲掉了。
具体做法是,补偿任务改成只在一个实例执行 调度层加 leader 选举;执行前用 setnx 抢一个带过期时间的锁 锁的粒度是整个批次;抢不到锁直接返回 不要降级成无锁执行;补偿成功后在流水表按订单号加唯一索引兜底。
最后用这个标准收尾,同一批次只有一台实例打印开始日志 退款流水按 order_no 分组计数都是 1。
