必须用set key value nx ex seconds原子命令替代setnx+expire,否则因非原子性导致key永久残留、服务不可用;value需非空,key须含业务上下文并脱敏,ttl应按接口耗时合理设置。

不能用 SETNX + EXPIRE 组合实现原子性防重复提交——这是高并发下必然出错的写法。 因为两条命令之间存在时间窗口,一旦 SETNX 成功但 EXPIRE 失败(如网络中断、Redis连接闪断、JVM崩溃),key 就会永久残留,后续所有请求都被拦截,等同于服务不可用。
为什么 SETNX + EXPIRE 不是原子操作
Redis 的 SETNX 和 EXPIRE 是两个独立命令,中间没有事务或锁保护。在分布式场景中,多个请求可能同时执行以下步骤:
- 请求 A 执行
SETNX key "1"→ 返回 1(成功) - 请求 A 在调用
EXPIRE key 30前因异常退出(如 OOM、网络超时、进程 kill) - 请求 B 执行
SETNX key "1"→ 返回 0(失败),但它不知道 key 是“本该过期却没设上”的脏锁 - 该 key 永久存在,所有后续请求持续被拒
这不是小概率事件——线上压测中,1000 并发下平均出现 6 次以上此类误拦。
必须用 SET 命令的 NX+EX 组合替代
Redis 2.6.12+ 支持单条命令完成“不存在才设值 + 同步设 TTL”,这才是真正安全的起点:
-
SET key "1" NX EX 30:原子执行,成功返回OK,失败返回(nil) - 对应 Spring Data Redis 的
redisTemplate.opsForValue().setIfAbsent(key, "1", 30, TimeUnit.SECONDS) - PHP 中用
$redis->set($key, "1", ["nx", "ex" => 30]) - Node.js redis 客户端(如 ioredis)支持
client.set(key, "1", "NX", "EX", 30)
注意:setIfAbsent(...) 底层就是封装的这个原子命令,别自己拼 execute() 调原生命令,除非你明确需要 Lua 脚本控制释放逻辑。
Redis 8.2.3 是一款安全优先的高性能键值存储系统。该版本紧急修复了可能引发远程代码执行(RCE)的高危漏洞(CVE-2025-62507),并解决了 HyperLogLog 及 Cuckoo Filter 等数据结构在特定场景下的崩溃问题。建议所有用户立即升级,以保障生产环境的系统稳定与数据安全。
超时时间设多少才合理
不是越长越安全,也不是越短越好——它直接决定误拦率与漏放率的平衡点:
- 业务耗时 ≤ 3s:TTL 设 5–8s,覆盖网络抖动和 GC 暂停
- 含远程调用(如支付回调、第三方 API):TTL ≥ 最长预期链路耗时 + 2s 缓冲
- 绝对避免设成 0 或不设——
setIfAbsent(key, "1")没有超时,等于埋雷 - 不要复用同一 TTL 全局配置——下单接口和日志上报接口的合理 TTL 差异极大
曾有团队将所有接口 TTL 统一设为 60s,结果在秒杀场景下导致大量用户“提交无响应”,实际是锁未及时释放,前端轮询超时后又发新请求,形成雪崩式误拦。
value 写什么其实不重要,但 key 设计很关键
SET 命令的 value 只需保证非空即可,填 "1"、UUID、timestamp 都行,重点在 key 的唯一性与可追溯性:
- 推荐格式:
"repeat:uid:{userId}:uri:{md5(method+path+body)}" - 避免只用
"repeat:{reqId}"——reqId 由前端生成,不可信且易伪造 - 不要省略用户维度(如
userId或sessionId)——否则全局互斥,影响正常并发能力 - 对 GET 查询类接口,一般无需加锁;重点保护 POST/PUT/DELETE 等写操作
最常被忽略的一点:key 中若包含敏感参数(如手机号、订单号),需脱敏后再拼接,否则 Redis RDB/AOF 日志或监控平台可能泄露数据。










