Redis HSETEX 命令详细教程
HSETEX在设置 Hash 字段值的同时,可选择性地为这些字段设置过期时间。它从 Redis 8.0.0 起提供,把“写入”和“设置字段 TTL”合并为一条原子命令。
资料合集:https://pan.quark.cn/s/10e98d308913、https://pan.quark.cn/s/f56bc69c5338
一、概览与语法
HSETEX key [FNX | FXX] [EX seconds | PX milliseconds | EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL] FIELDS numfields field value [field value ...]| 项目 | 说明 |
|---|---|
| 数据类型 | Hash |
| 支持版本 | Redis 8.0.0 起 |
| key | Hash 的 Key |
| 条件选项 | FNX 或 FXX,两者互斥 |
| 过期选项 | EX、PX、EXAT、PXAT、KEEPTTL,五者互斥 |
| FIELDS | 必填关键字,不可省略 |
| numfields | 字段数量,必须与后续字段值对个数一致 |
| 返回值 | 整数,0 表示未设置任何字段,1 表示全部字段已设置 |
| 时间复杂度 | O(N),N 为设置的字段数量 |
| ACL | @write、@hash、@fast |
| 命令标记 | write、denyoom、fast |
官方说明指出:如果 Key 已持有值,会被覆盖,且与 Key 关联的先前 TTL 会被丢弃。$TRAE_REF
二、条件选项 FNX 与 FXX
| 选项 | 含义 |
|---|---|
| FNX | 仅当这些字段都尚不存在时才设置 |
| FXX | 仅当这些字段都已存在时才设置 |
| 不写 | 无条件写入,覆盖已有值 |
两者互斥,不能同时使用。注意判断单位是“字段集合”而非单个字段:FNX 要求所有指定字段都不存在,FXX 要求所有指定字段都已存在。这与 HSETNX 只针对单个字段不同。
三、过期选项
五个过期选项互斥,不能组合,否则报错。
| 选项 | 含义 |
|---|---|
| EX seconds | 从当前起经过指定秒数后到期 |
| PX milliseconds | 从当前起经过指定毫秒后到期 |
| EXAT unix-time-seconds | 在指定 Unix 秒级时间戳到期 |
| PXAT unix-time-milliseconds | 在指定 Unix 毫秒级时间戳到期 |
| KEEPTTL | 保留字段已有的 TTL,不改变 |
| 不写 | 按默认行为处理,写入会清除原 TTL |
官方示例中演示了组合冲突的错误:同时写EX 60 KEEPTTL会返回错误ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified。$TRAE_REF
四、基础示例
以下命令需要 Redis 8.0 或更新版本,在测试实例的 redis-cli 中执行。文中结果是预期说明,未实际连接 Redis 运行。示例沿用官方示例的结构。
DEL tutorial:{hsetex}:mykey HSETEX tutorial:{hsetex}:mykey EXAT 1740470400 FIELDS 2 field1 Hello field2 World HTTL tutorial:{hsetex}:mykey FIELDS 2 field1 field2 HSETEX tutorial:{hsetex}:mykey FNX EX 60 FIELDS 2 field1 Hello field2 World HSETEX tutorial:{hsetex}:mykey FXX EX 60 KEEPTTL FIELDS 2 field1 hello field2 world HSETEX tutorial:{hsetex}:mykey FXX KEEPTTL FIELDS 2 field1 hello field2 world HTTL tutorial:{hsetex}:mykey FIELDS 2 field1 field2预期结果:第一条 HSETEX 返回1,两个字段被写入并带有指定的绝对到期时间;HTTL 返回两个相同的剩余秒数。第二次调用使用 FNX,但字段已存在,返回0,未做任何修改。第三次同时指定 EX 与 KEEPTTL,报参数冲突错误。第四次仅用 KEEPTTL,返回1,写入新值并保留原 TTL;HTTL 仍返回正数。
如果示例中的绝对时间戳已经过去,第一次调用会导致字段立即删除,HTTL 返回 -2。实际使用时请替换为未来的时间戳。
五、返回值语义
返回值只有两种,信息量有限:
| 返回值 | 含义 |
|---|---|
| 1 | 所有字段都被设置 |
| 0 | 没有任何字段被设置(条件不满足) |
它不告诉你新增了几个字段(这与 HSET 不同),也不区分“部分字段因条件失败”。因为 FNX/FXX 的判断单位是整个字段集合,要么全部设置,要么全部不设置。需要知道新增字段数量时应使用 HSET。
六、边界情况与错误处理
| 场景 | 行为 |
|---|---|
| Key 不存在且未写 FXX | 创建 Hash 并写入字段 |
| Key 不存在且写了 FXX | 条件不满足,返回 0,不创建 Key |
| 字段部分存在、使用 FNX | 条件不满足,返回 0,不写入任何字段 |
| 字段部分缺失、使用 FXX | 条件不满足,返回 0,不写入任何字段 |
| 同时指定两个过期选项 | 报参数冲突错误 |
| EXAT/PXAT 传入过去的时间 | 字段被写入后立即到期删除 |
| numfields 与实际字段值对不符 | 报语法错误 |
| Key 是 String、List 等非 Hash | 报 WRONGTYPE 错误 |
注意 FNX 与 SET 的 NX 语义不同:SET NX 针对整个 Key,HSETEX FNX 针对字段集合。不要把两者混为一谈。
七、Python 客户端示例
前提为已安装 redis-py 且服务端为 Redis 8.0 或更新版本。使用通用接口显式展示 FIELDS 语法,避免依赖客户端是否提供专用方法。
importtimeimportredis r=redis.Redis(host="localhost",port=6379,decode_responses=True)k="tutorial:{hsetex}:python"try:r.delete(k)# 写入两个字段并设置 300 秒 TTLprint(r.execute_command("HSETEX",k,"EX",300,"FIELDS",2,"a","A","b","B"))# 1print(r.execute_command("HTTL",k,"FIELDS",2,"a","b"))# 约 [300, 300]# FNX:字段已存在,条件不满足print(r.execute_command("HSETEX",k,"FNX","EX",60,"FIELDS",2,"a","X","b","Y"))# 0# FXX + KEEPTTL:字段都存在,写入新值并保留 TTLprint(r.execute_command("HSETEX",k,"FXX","KEEPTTL","FIELDS",2,"a","X","b","Y"))# 1print(r.hgetall(k))# {'a': 'X', 'b': 'Y'}print(r.execute_command("HTTL",k,"FIELDS",2,"a","b"))# 仍为正数finally:r.delete(k)r.close()八、与相近命令的区别
| 命令 | 设置值 | 设置字段 TTL | 条件 | 起始版本 |
|---|---|---|---|---|
| HSET | 是 | 否(且清除原 TTL) | 无 | 2.0 |
| HSETNX | 是 | 否 | 单字段不存在 | 2.0 |
| HSETEX | 是 | 是 | FNX / FXX | 8.0 |
| HEXPIRE | 否 | 是 | NX / XX / GT / LT | 7.4 |
| HGETEX | 否(读取) | 是 | 无 | 8.0 |
HSETEX 填补了“写入并设置字段 TTL”这一步的原子性缺口。在 Redis 8.0 之前,实现同样效果需要 HSET 加 HEXPIRE 两条命令,中间存在竞态窗口。
九、并发、原子性与典型场景
HSETEX 的核心价值在于把写入与设置字段 TTL 合并为一条原子命令。用 HSET 加 HEXPIRE 两条命令实现时,中间会有窗口期:其他客户端可能在此期间读取到“值已更新但没有 TTL”的中间状态,或者你设置的 TTL 被并发写入清除。
典型场景:写入带时效的会话字段、设置短时验证码、缓存字段并同时设定过期、需要条件写入(字段都不存在才写)的初始化逻辑。
使用相对时间选项(EX、PX)时,重试会从新的时刻重新计时,可能无意延长有效期;需要固定截止时间时应使用 EXAT 或 PXAT。请求超时也不代表未执行,重发前可先用 HTTL 或 HGETALL 检查字段当前状态。
十、练习、排错与总结
练习:新建tutorial:{hsetex}:exercise,用HSETEX ... EX 300 FIELDS 2 a 1 b 2写入两个字段,确认返回1;用 HTTL 确认两个字段都有正数 TTL;再用FNX EX 60 FIELDS 2 a 9 b 9调用,确认返回0且值未被修改;最后用FXX KEEPTTL FIELDS 2 a 9 b 9调用,确认返回1、值已更新且 TTL 保留。
排错要点:unknown command 时确认服务端版本不低于 8.0;返回 0 检查 FNX/FXX 条件是否满足(注意判断单位是整个字段集合);报参数冲突时检查是否同时写了两个过期选项;报语法错误时检查 FIELDS 关键字与 numfields 数值;写入后 TTL 意外消失说明使用了不带 KEEPTTL 的默认写入;字段被立即删除说明 EXAT/PXAT 传入了过去的时间。清理使用DEL tutorial:{hsetex}:mykey tutorial:{hsetex}:exercise。速记:8.0 起支持、写入与设置字段 TTL 原子完成、FNX/FXX 针对字段集合、五个过期选项互斥、返回 0 或 1、KEEPTTL 保留原 TTL。