Redis String设TTL应直接用SET+EX/PX原子命令,避免SET+EXPIRE分步操作;SETEX不兼容集群且仅支持秒级;校验需GETDEL或Lua保证原子性;限频要用INCR配合条件EXPIRE。

Redis String 设置 TTL 的正确姿势
直接用 SET 命令加 EX 或 PX 参数,是设置验证码过期最稳妥的方式。别先 SET 再 EXPIRE,中间可能被并发读取到未设过期的值。
-
SET verify:138****1234 "a7f9" EX 300—— 5 分钟后自动删除,推荐用于短信验证码 -
SET verify:email:user@example.com "b3e8" PX 180000—— 毫秒级控制,适合需要更精确超时的场景 - 避免分两步:
SET后立刻EXPIRE,网络延迟或 Redis 主从同步间隙可能导致 key 暂时无过期时间
为什么不用 SETEX?它和 SET+EX 有啥区别
SETEX 看似简洁,但它是 Redis 早期命令,在集群模式(Redis Cluster)下不支持,会报 CROSSSLOT 错误;而 SET ... EX 是原子命令,全版本兼容,语义也更清晰。
-
SETEX只支持秒级EX,不能设毫秒(PX),灵活性差 - 部分 Redis 客户端(如某些旧版 Jedis)对
SETEX的异常处理不如SET统一 - 新项目一律用
SET key value EX ttl_seconds,兼容性、可读性、可维护性都更好
验证码被重复校验或提前失效的常见原因
不是 TTL 设错了,往往是业务逻辑没兜住:比如校验成功后没删 key,或删了却没加原子性保护,导致并发请求重复消费。
- 校验时用
GETDEL(Redis 6.2+)或EVAL脚本,确保“读 + 删”原子执行,避免两次请求都拿到同一验证码 - 如果用
GET+DEL两步,第二个请求可能在第一个DEL前就读到值,造成重复使用 - TTL 过短(如 60 秒)+ 网络延迟高,用户点发送后等几秒才收到,实际只剩 10 秒可填,体验差——建议短信类至少 300 秒,邮箱类可放宽到 900 秒
用 INCR + 过期控制发送频率时要注意什么
限制单手机号 60 秒内最多发 3 条验证码,得靠 INCR 和 EXPIRE 配合,但这里有个关键陷阱:EXPIRE 必须在 INCR 返回 1 的时候才调,否则每次都会重置过期时间。
- 正确做法:先
INCR sms:limit:138****1234,若返回 1,则立即EXPIRE sms:limit:138****1234 60 - 错误写法:不管返回值,每次都
EXPIRE,导致限频窗口永远被拉长 - 注意客户端连接断开时
INCR成功但EXPIRE失败,可用 Lua 脚本封装成原子操作,例如EVAL "if redis.call('INCR', KEYS[1]) == 1 then redis.call('EXPIRE', KEYS[1], ARGV[1]) end" 1 sms:limit:xxx 60










