一次性取值模式是什么
验证码、单次令牌这类「读过即作废」的数据,需要取出即删除的原子语义:拆成两条命令(先读再删)会在并发下出现同一凭据被使用两次的窗口。取值即删除命令把两步合成一步;需要「读的同时续期」则用取值加改过期时间的命令。
基本信息
| 取值即删 | GETDEL,Redis 6.2 起支持,返回旧值并删除键 |
|---|---|
| 取值并改过期 | GETEX,返回旧值并设置或取消过期时间 |
| 旧版本替代 | 用 Lua 脚本把取值与删除写在一段脚本里 |
| 过期参数 | 可选秒、毫秒、绝对时间戳与取消过期几种写法 |
| 语义要点 | 键不存在时返回空值,不报错 |
典型使用场景
- 短信与邮件验证码:校验通过后取值即删,防止同一码重复使用
- 一次性登录令牌:换取会话后立即作废,避免重放
- 防重复提交:用一次性标记键拦住重复点击
- 会话滑动续期:读取会话的同时延长过期时间,减少一次往返
- 幂等控制:处理完成前先取用一次性令牌,天然避免并发重复执行
验证码校验流程
验证码校验流程
SET sms:login:13900000000 888888 EX 300 # 写入验证码,5 分钟有效
# 校验通过后原子取出并删除,防止重放
GETDEL sms:login:13900000000 # 返回 888888,同时删除键
GET sms:login:13900000000 # 返回空,说明已被消费
# 会话滑动续期:读取的同时延长过期时间
GETEX session:web:u88 EX 1800 # 每次访问续期 30 分钟
# 旧版本或需要更复杂判断时用 Lua
EVAL "local v = redis.call('GET', KEYS[1]); if v then redis.call('DEL', KEYS[1]) end; return v" 1 sms:login:13900000000命令对照
| 方式 | 原子性 | 适用 |
|---|---|---|
| 取值即删 | 单命令原子 | 一次性凭据消费 |
| 取值加改过期 | 单命令原子 | 读取时续期或提前失效 |
| 先读再删两条命令 | 非原子,有并发窗口 | 不建议用于凭据类场景 |
| Lua 脚本 | 整脚本原子 | 需要额外判断条件的场景 |
绝对时间戳参数容易算错单位:写成本地时间戳或误用秒与毫秒,会导致键立刻过期或永不过期。
凭据类数据一旦被并发消费两次,业务侧会出现重复登录或重复扣款,必须保证单命令原子或整脚本原子。