当前位置:首页 > 文章列表 > 数据库 > Redis > Redis SET 的 GET 选项怎么原子取得旧值

Redis SET 的 GET 选项怎么原子取得旧值

来源:17golang原创 2026-09-28 03:03:00 0浏览 收藏

直接使用 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 命令,中间不会出现另一个命令可见的插入位置。

客户端、SET 命令、GET 选项、字符串键、新值与旧值响应的静态关系图
图1:SET 与 GET 选项的静态调用关系说明图。命令携带新值写入字符串键,同时把覆盖前的旧值作为响应;此图不是运行截图。

这条命令的复杂度是 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
SET GET、默认写入、KEEPTTL、EX、PX 与新旧 TTL 的静态关系图
图2:SET GET 与 TTL 选项关系静态说明图。KEEPTTL 保留原 TTL,EX 或 PX 设置新过期时间,未给过期选项时成功写入会清除旧 TTL;此图不是运行截图。

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 版本和客户端响应映射即可。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
tuozi工具箱版本怎么看?官网标注、6.0.1与更新日期核对tuozi工具箱版本怎么看?官网标注、6.0.1与更新日期核对
上一篇
tuozi工具箱版本怎么看?官网标注、6.0.1与更新日期核对
Go slog.LevelVar 怎么运行时调整日志级别
下一篇
Go slog.LevelVar 怎么运行时调整日志级别
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    246次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    292次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    261次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    242次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    50次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码