报错是这么冒出来的。用 Redis 记幂等标记,SETNX 加二十四小时过期,实例开了四 GB 上限和 allkeys-lru,大促时缓存被别的业务写满,幂等键被提前淘汰,同一批回调在十分钟内重复执行了三千多次。
中间走过弯路,把「过期时间只按一天设,没有按实际去重窗口收敛」当成主因,改完现象没变。
原因不止一个,得分开看。allkeys-lru 会把幂等键和普通缓存一起淘汰。还有一种是幂等数据和其他业务共用同一个实例。
另外,淘汰之后没有业务侧唯一约束兜底。
第一件事是看实例的淘汰策略和已淘汰键数量
redis-cli CONFIG GET maxmemory-policy; redis-cli INFO stats | Select-String evicted_keys
然后确认幂等键还在不在以及剩余过期时间
redis-cli --scan --pattern 'idem:*' | Select-Object -First 5; redis-cli TTL idem:ORDER-8842
接着把幂等数据迁到独立实例并关闭淘汰
redis-cli -h 10.0.3.21 CONFIG SET maxmemory-policy noeviction
库表补唯一约束承接重复写入
psql -c "ALTER TABLE callback_log ADD CONSTRAINT uq_trace UNIQUE (trace_id);"
别只看报错消失,验证要多做一步。重放同一 trace_id 的回调被唯一约束挡住,压测中幂等键没有被淘汰,重复执行条数归零。
把幂等标记当普通缓存放在 LRU 实例里,等于把去重交给淘汰策略决定,关键状态必须单独存放。这类问题最好一次改到位,反复小改反而容易把原因搅混。
