登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  Redis

Redis SET GETEX 一次读写 TTL 时如何避免竞态

来源:17golang原创

时间:2026-09-14 12:20:58 303浏览 收藏

缓存代码里常见一个小陷阱:先 SET,再单独执行 EXPIRE,或者先 GETEXPIRE。中间只要遇到超时、覆盖或并发请求,值和 TTL 就可能不同步。更稳妥的做法是写入时使用 SET key value EX seconds,读取并续期时使用 GETEX key EX seconds;两个命令各自把相关动作放在一次 Redis 命令里完成。

要点速览
  • SET ... EX/PX 负责“写值时一起设置 TTL”,不要把 EXPIRE 作为事后补丁。
  • GETEX ... EX/PX 负责“返回字符串值时刷新 TTL”,不存在的键返回空值。
  • 单命令原子性不等于多键业务事务;还要做条件判断、跨键更新时,使用事务或 Lua 脚本。

一、先把写入过期和读取续期分开

“设置缓存并让它五分钟后过期”和“读到缓存后再延长五分钟”是两个动作。前者用 SET,后者用 GETEX。不要把它们理解成一个叫“SET GETEX”的组合语法:Redis 命令仍然是分别发送的,只是每条命令内部的值操作与 TTL 操作不会被另一个客户端插入。

场景推荐命令返回/检查重点
首次写入或覆盖缓存SET key value EX 300返回 OK,再用 TTL 抽查
读取并滑动续期GETEX key EX 300返回值或空值,TTL 同时更新
只读不续期GET key不会改变 TTL
Redis SET 与 GETEX 的字符串键和 TTL 静态边界关系示意图
图1:操作示意图,展示 SET 写入值与 TTL、GETEX 读取值与刷新 TTL 的静态职责边界。

二、写入时用 SET EX,别让值先裸奔

缓存回源成功后,直接把过期参数放进 SETEX 使用秒,PX 使用毫秒;两者选一个即可。这样不会出现 SET 已经成功、进程却在 EXPIRE 前崩溃的窗口。

# 写入字符串,并在同一条命令里设置 300 秒 TTL
SET user:42:profile profile-v1 EX 300

# 只允许不存在的键被创建;条件不满足时不会覆盖旧值
SET lock:order:42 token-abc NX PX 5000

如果业务要求“只有键不存在才写入”,把 NX 和 TTL 一起放进同一条 SET。不要先 EXISTSSET,那会重新暴露检查与写入之间的竞态。

三、读取时用 GETEX,滑动过期只做一次往返

GETEX 类似 GET,但它是写命令,会在返回字符串值的同时修改 TTL。下面的写法适合会话、短期授权缓存或“访问后再保留五分钟”的滑动窗口。

# 返回缓存内容,并把有效期重新设为 300 秒
GETEX user:42:profile EX 300

# 只查看内容,不延长生命周期
GET user:42:profile

# 需要诊断时再检查 TTL;-1 表示无过期,-2 表示键不存在
TTL user:42:profile

GETEX 的过期选项互斥,可用 EXPXEXATPXATPERSIST。键不存在时返回空值,不会凭空创建键。若只是想取消过期,可使用 GETEX key PERSIST,但这会改变缓存生命周期,不能当作普通读取。

Redis GETEX 读取字符串并更新 EX TTL 的静态关系示意图
图2:结果示意图,区分 GETEX 的返回值、TTL 更新、键不存在和只读 GET 四种状态。

四、用返回值和 TTL 检查真正的问题

排查时不要只看业务日志里的“缓存命中”。把结果拆成两个维度:返回值判断键是否存在,TTL 判断是否仍有过期策略。

  • 返回空值且 TTL-2:键已不存在,通常应回源。
  • 返回非空但 TTL-1:值存在却没有过期时间,检查是否有旧代码使用了普通 SET 覆盖了 TTL。
  • TTL 比预期小很多:确认客户端传的是秒还是毫秒,并检查是否有其他读取路径在刷新。

这类抽查命令只是诊断动作,不要在高并发请求路径里为每次命中额外发送 TTL,否则又增加了一次网络往返。

五、单命令安全,不代表复合业务自动安全

SET ... NX EXGETEX ... EX 能保证各自命令内部的动作连续执行,但不能替代“读值、比较版本、更新另一个键”的事务。如果你要根据旧值决定是否改写多个键,使用 MULTI/EXEC 配合乐观锁,或把完整判断放进 Lua 脚本。

另外,GETEX 从 Redis 6.2 才提供;连接到更旧的服务端时应先确认版本,再考虑用客户端兼容方案。若只能拆成 GETEXPIRE,至少要记录失败、重试和键被覆盖的情况,不要把两个成功率相乘后当成原子操作。

相关问题

GETEX 会把 TTL 延长到原来的剩余时间再加五分钟吗?

不会。使用 EX 300 时是把过期时间重新设为当前命令指定的 300 秒窗口,不是自动累加剩余 TTL。

SET EX 和 SETEX 有什么区别?

SET key value EX seconds 更容易与 NXXX 等条件组合;新代码优先使用它。两者都能在写入字符串时设置秒级过期。

GETEX 能读取 Hash 字段吗?

不能。GETEX 面向字符串键;Hash 字段需要使用对应的 Hash 命令或重新设计缓存粒度。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>