Redis SET 的 GET 选项怎么原子取得旧值
直接使用 SET key new-value GET。Redis 会在一次命令中把新值写入字符串键,并把覆盖前的旧字符串作为响应返回;键原来不存在时返回 nil。这个写法把“读取旧值”和“写入新值”放进同一个原子操作,避免客户端先发 GET、再发 SET 时被其他写入插队。
官方文档:https://redis.io/docs/latest/commands/set/
分开的 GET 和 SET 留下了并发窗口
如果业务只在单机测试,先读再写看起来没有问题:
# 先读取旧库存 redis-cli GET inventory:sku-42 # 再覆盖为新库存 redis-cli SET inventory:sku-42 18
问题在于这其实是两条独立命令。客户端拿到旧值后、发出 SET 前,另一个客户端可能已经改过同一个键。第一个客户端最终覆盖的是最新值,却拿着更早的旧值继续做审计、补偿或通知,读写语义就被拆散了。
MULTI/EXEC 可以把多个命令排队执行,但为了“覆盖并返回旧值”专门创建事务没有必要。Redis 6.2 起,SET 自带的 GET 选项已经能直接表达这个动作。
一条 SET 同时完成写入和旧值返回
最小写法只是在普通 SET 后面增加 GET:
# 先准备一个旧值 redis-cli SET inventory:sku-42 21 # 写入 18,并在同一条命令中返回旧值 21 redis-cli SET inventory:sku-42 18 GET # 核对键当前保存的新值 redis-cli GET inventory:sku-42
第二条命令的响应是旧值 21,第三条命令读到的是新值 18。关键点不是少一次网络往返,而是旧值读取与新值写入属于同一条 Redis 命令,中间不会出现另一个命令可见的插入位置。

这条命令的复杂度是 O(1)。在 RESP2 中,旧值以 bulk string 返回;原键不存在时返回 null bulk string。不同客户端库会把 nil 映射为各自的空响应、空对象或特定错误,因此业务代码要按客户端文档判断“键不存在”,不要把空字符串和 nil 混在一起。
三个返回边界必须先想清楚
键原来不存在
命令仍会创建键并写入新值,但因为不存在旧值,所以 GET 返回 nil:
# 确保示例键不存在 redis-cli DEL session:token:42 # 创建键;响应为 nil,而键的新值已经写入 redis-cli SET session:token:42 token-b GET # 核对新值 redis-cli GET session:token:42
因此,nil 表示“命令执行前没有这个键”,不表示 SET 一定失败。
旧值不是字符串
GET 选项要求旧值能按字符串返回。如果键当前保存的是列表、集合、哈希等非字符串类型,Redis 会返回 WRONGTYPE 错误,并中止这次 SET;旧数据不会被覆盖。这个行为比普通 SET 更严格:普通 SET 可以无视原类型直接覆盖,而 SET GET 必须先取得旧字符串。
# 创建一个列表类型的键 redis-cli RPUSH queue:jobs job-1 # 旧值不是字符串,这次写入会报错并中止 redis-cli SET queue:jobs replaced GET
GET 与 NX、XX 一起使用
条件选项负责决定是否写入,GET 负责返回命令执行前的值。当前官方返回值说明明确指出:指定 GET 后,只要旧键存在,就会返回旧值,无论条件最终是否让新值写入;旧键不存在则返回 nil。也就是说,单靠响应里的旧值不能证明条件写入成功。
Redis 7.0 起才允许 NX 与 GET 一起使用。如果应用需要组合这些选项,要先核对服务端版本,并在条件写入场景中用额外业务状态或后续读值确认结果,不能沿用“返回非 nil 就代表写入成功”的判断。
TTL 要明确选择保留还是重设
成功执行普通 SET 会清除键原来的过期时间。增加 GET 只改变返回值,不会自动保留 TTL。如果这个键原来会过期,更新时必须明确选择以下三种语义之一:
| 写法 | TTL 结果 | 适合场景 |
|---|---|---|
SET key value GET | 清除旧 TTL,变为持久键 | 新值应长期保留 |
SET key value GET KEEPTTL | 保留已有 TTL | 只换值,不改变原到期时刻 |
SET key value GET EX 60 | 重新设置 60 秒 TTL | 更新值时重置有效期 |
SET key value GET PX 1500 | 重新设置 1500 毫秒 TTL | 需要毫秒级有效期 |
# 保留会话键原有的到期时间,同时取得旧令牌 redis-cli SET session:token:42 token-c GET KEEPTTL # 写入新缓存值,并把有效期重新设为 60 秒 redis-cli SET cache:profile:42 v3 GET EX 60

EX、PX、EXAT、PXAT 与 KEEPTTL 属于同一组互斥的过期选项,一条 SET 只能选择其中一种。不要同时写 KEEPTTL 和 EX,希望 Redis “先保留再延长”;那不是合法组合。
从 GETSET 迁移到 SET GET
旧代码常用 GETSET key value 完成相同的基础替换。Redis 官方 SET 文档说明,SET 的选项能够替代 GETSET。迁移时可以直接改成:
# 旧写法:写入新值并返回旧值 redis-cli GETSET counter:snapshot 1024 # 新写法:Redis 6.2 及以上使用 SET GET redis-cli SET counter:snapshot 1024 GET
新写法的优势是可以继续组合 KEEPTTL 或 EX、PX 等过期选项,命令语义集中在 SET 上。兼容边界也很明确:GET 选项从 Redis 6.2 开始提供;如果集群中仍有更早版本的节点,必须先完成版本核对和升级,再替换客户端命令。
采用时按这张清单检查
- 只在确实需要旧值时使用 GET:不需要旧值的普通覆盖继续用 SET,语义更直接。
- 把 nil 当成“此前不存在”:它不等同于空字符串,也不自动代表写入失败。
- 先确认旧键类型:非字符串旧值会让 SET GET 报错并中止。
- 显式决定 TTL:默认清除、KEEPTTL 保留、EX/PX 重设,三者不要混淆。
- 条件写入不要只看旧值:组合 NX 或 XX 后,GET 返回值不能单独证明是否写入。
- 服务端至少 Redis 6.2:NX 与 GET 的组合还需要 Redis 7.0 或更高版本。
相关问题
SET GET 返回的是新值还是旧值?
返回命令执行前保存的旧字符串。新值已经写进键中,之后再 GET 才会读到新值。
键不存在时为什么返回 nil,但键又被创建了?
GET 描述的是旧状态。旧键不存在,所以没有旧值可返回;与此同时,SET 仍按正常语义创建键并写入新值。
SET GET 会自动保留过期时间吗?
不会。成功 SET 默认清除旧 TTL。需要保持原到期时刻时增加 KEEPTTL,需要重置有效期时选择 EX、PX、EXAT 或 PXAT。
为什么不直接继续用 GETSET?
SET GET 能表达相同的基础替换,还能与 SET 的过期和条件选项组合。新项目优先统一到 SET 语法,旧项目迁移前确认 Redis 版本和客户端响应映射即可。
tuozi工具箱版本怎么看?官网标注、6.0.1与更新日期核对
- 上一篇
- tuozi工具箱版本怎么看?官网标注、6.0.1与更新日期核对
- 下一篇
- Go slog.LevelVar 怎么运行时调整日志级别
-
- 数据库 · Redis | 4小时前 | Redis ·
- Redis 分片 Pub/Sub 与普通 Pub/Sub 有什么区别
- 327浏览 收藏
-
- 数据库 · Redis | 7小时前 |
- Redis LATENCY DOCTOR 怎么判断延迟来源
- 169浏览 收藏
-
- 数据库 · Redis | 10小时前 |
- Redis ACL 怎么同时限制命令和键前缀
- 244浏览 收藏
-
- 数据库 · Redis | 12小时前 |
- Redis Functions 怎么替代需要重复加载的 Lua 脚本
- 177浏览 收藏
-
- 数据库 · Redis | 1天前 |
- Redis 有序集合按分值和字典序查询有什么区别
- 411浏览 收藏
-
- 数据库 · Redis | 1天前 | Redis · 消息队列 · Stream · redis Redis Stream XAUTOCLAIM PEL
- Redis XAUTOCLAIM 怎么接管长时间未确认消息
- 193浏览 收藏
-
- 数据库 · Redis | 1天前 |
- Redis 客户端缓存 OPTIN 与 OPTOUT 怎么选
- 109浏览 收藏
-
- 数据库 · Redis | 2天前 | Redis · Stream ·
- Redis Pub/Sub断线期间消息丢失时的替代结构
- 369浏览 收藏
-
- 数据库 · Redis | 4天前 |
- Redis SCAN遍历大Key空间并避免阻塞的实践
- 422浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 246次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 292次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 261次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 242次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 50次使用
-
- Redis Stream XTRIM 如何避免消费组积压无限增长
- 2026-09-12 501浏览
-
- Redis AOF rewrite 期间如何判断磁盘与内存压力
- 2026-09-12 501浏览
-
- Redis RDB 和 AOF 怎么按可接受数据丢失量选择
- 2026-09-08 501浏览
-
- Redis XAUTOCLAIM 之后为什么仍有 pending:JUSTID、PEL 与消息删除边界
- 2026-08-29 501浏览
-
- Redis SET 的 GET 与 KEEPTTL 怎么一起验收:旧值返回、续期与回滚边界
- 2026-08-20 501浏览

