核心方案:使用Redis的String类型存储请求参数的唯一标识(如用户ID+时间戳+随机数生成的key),设置过期时间(如300秒)。接口接收请求时,先用setnx命令尝试设置key,如果成功(返回1),则执行业务逻辑并记录结果;如果失败(返回0),则直接返回已处理结果。防止重复请求导致数据不一致。
方案一:基础幂等性检查
1. 生成幂等key:通常使用用户ID + 接口路径 + 请求参数MD5拼接。2. Redis setnx key value nx ex 300,如果设置成功,执行业务。3. 业务完成后,key设为"processed"。重复请求时,setnx失败,直接返回"已处理"。这样保障了接口安全,避免重复扣款等。
方案二:结合Lua脚本增强
使用Redis Lua脚本原子执行:local key = KEYS[1] local val = ARGV[1] if redis.call('setnx', key, val) == 1 then redis.call('expire', key, 300) return 1 else return 0 end。接口调用EVAL脚本,如果返回1则处理,否则拒绝。Lua确保原子性,防止并发问题。
方案三:分布式锁模式
对于复杂业务,用Redis分布式锁:set key value nx px 30000(30秒锁)。获取锁成功后执行业务,完成后del锁(用Lua验证value释放)。幂等key单独存结果。保障高并发下数据一致性,如秒杀接口。
方案四:Hash存储多参数
对复杂参数,用HSET存储参数hash,key为接口幂等ID。检查时用HEXISTS,若存在则跳过。新请求HSET全参数并EXPIRE。这样支持部分参数幂等,灵活保障一致性。
方案五:实际代码示例
public boolean idempotentCheck(String idempotentKey) { String result = redisTemplate.opsForValue().setIfAbsent(idempotentKey, "1", 300, TimeUnit.SECONDS); return "OK".equals(result); } if(idempotentCheck(key)){ //执行业务 }
方案六:pipeline优化性能
高QPS场景,用pipeline批量:setnx + expire原子,但pipeline非原子,用Lua更好。结合watch多key乐观锁,保障极端并发安全。
方案七:监控与扩展
用Redis SCAN监控过期key,AOF持久化结果。扩展到MQ幂等:消息key存Redis,重复消费查重。
FAQ
Q: 幂等key怎么生成?
A: 用户ID + timestamp + random + MD5(request params)。
Q: 过期时间设置多少?
A: 接口耗时x2,如支付5min。
Q: Redis单点故障怎么处理?
A: 用哨兵或集群,key一致性hash。
Q: 支持异步接口吗?
A: 是,用结果key存回调地址,重复查结果。
Q: 性能瓶颈?
A: Lua脚本+连接池,单机10w QPS无压力。