Redis的SET命令必须正确使用EX参数(如SET key value EX 60)才能设置过期时间,漏写、错序或误作值都会导致永不过期;需结合随机偏移防雪崩、空值缓存防击穿,并优先用SET多参数而非SETEX以保障灵活性与兼容性。

用 SET 缓存查询结果时,为什么加了 EX 还是没过期?
Redis 的 SET 命令本身不支持直接设置过期时间,EX 是独立参数,不是可选开关——漏掉它或放错位置,缓存就永不过期。
-
SET key value EX 60✅ 正确:60 秒后自动删除 -
SET key value EX❌ 错误:Redis 报错ERR syntax error -
SET key value 60❌ 错误:60 被当成 value 的一部分,无过期效果
常见场景是 ORM 查询后拼接 key,比如用户详情:user:123。如果忘记写 EX 或参数顺序错,这个 key 就一直占内存,还可能返回脏数据。
缓存 key 设计不当,导致击穿或雪崩怎么办?
单纯用 user:id 这类静态 key,在高并发查同一条记录时,一旦失效,所有请求会穿透到数据库——这就是缓存击穿;若大量 key 同时过期(比如都设了 EX 300),就是雪崩。
- 加随机偏移:比如
EX 300 + Math.floor(Math.random() * 60),让过期时间分散在 ±60 秒内 - 避免全量刷新:不要批量
SET所有用户缓存并设统一EX,改用按需更新 + 独立过期 - 慎用空值缓存:查不到也
SET key "" EX 60,防穿透,但注意业务是否允许空响应
Node.js 示例:
redis.set(`user:${id}`, JSON.stringify(data), 'EX', 300 + Math.floor(Math.random() * 60))
用 SET 存对象时,JSON 序列化容易踩哪些坑?
数据库查出的对象直接 JSON.stringify() 再塞进 SET 很常见,但 Date、undefined、BigInt、循环引用这些会被静默丢弃或报错。
-
Date变成字符串没问题,但反序列化后是 string 不是 Date,业务层要自己转 -
undefined在 JSON 中被忽略,字段消失;建议用null占位或提前过滤 - MySQL 的
BIGINT主键(如雪花 ID)在 JS 里可能精度丢失,JSON.stringify()后再读出来可能变样 - 别对整个 result set 直接
JSON.stringify(rows),先做最小化裁剪(去掉不需要的字段)
Python 示例:
redis.set(f"user:{user_id}", json.dumps(user_dict, default=str), ex=300) —— default=str 是兜底,但不如明确处理类型
为什么不用 SETEX 而坚持用 SET ... EX?
SETEX 看似更短,但它在 Redis 6.2+ 已被标记为“legacy”,且语义固化:只能设秒级过期,不能组合 PX(毫秒)、NX(仅当不存在时)、GET(获取旧值)等选项。
- 需要原子性更新+返回旧值?用
SET key value EX 300 GET - 要毫秒级过期(比如 1500ms)?必须用
PX,SETEX不支持 - 想防止并发重复写入?加
NX,SETEX没这能力 - 未来升级 Redis 或对接新客户端时,
SET多参数形式兼容性更好
所以现在写缓存,统一走 SET key value [EX seconds] [PX milliseconds] [NX|XX] [GET] 这条路,灵活且面向未来。
复杂点在于:过期时间不是越长越好,也不是越短越安全;它得和你的数据变更频率、下游容忍延迟、内存水位一起算。最容易被忽略的是——没监控 key 的平均存活时长和命中率,光靠 EX 参数没法闭环验证缓存是否真在起作用。


















