Redis HPERSIST 命令详细教程
HPERSIST 移除 Hash 中一个或多个字段的过期时间,把字段从"易失"变为"永久"。它从 Redis 7.4.0 起提供,只读不写数据本身,只改动过期元数据。
资料合集:https://pan.quark.cn/s/10e98d308913、https://pan.quark.cn/s/f56bc69c5338
一、概览与语法
redis
HPERSIST key FIELDS numfields field [field ...]
| 项目 | 说明 |
|---|---|
| 数据类型 | Hash 的字段级过期时间 |
| 支持版本 | Redis 7.4.0 起 |
| key | 一个 Hash Key |
| FIELDS | 必填关键字,不可省略 |
| numfields | 字段数量,必须与后续字段参数个数一致 |
| field | 至少一个字段名;不支持通配符 |
| 返回值 | 数组,元素顺序与输入字段一致 |
| 时间复杂度 | O(N),N 为指定字段数量 |
| ACL | @write、@hash、@fast |
| 命令标记 | write、fast |
命令描述中给出了术语定义:带有过期时间的字段称为 volatile(易失),移除后成为 persistent(永久,不再关联 TTL)。$TRAE_REF
二、逐字段返回值
| 数值 | 含义 |
|---|---|
| -2 | 该字段不存在,或整个 Key 不存在 |
| -1 | 该字段存在,但本来就没有过期时间 |
| 1 | 成功移除了该字段的过期时间 |
即使只指定一个字段也返回数组,不能当成单个整数处理。-1 与 -2 的区别很关键:前者表示字段存在且已是永久状态(操作无效果但数据正常),后者表示字段根本不存在。$TRAE_REF
三、基础示例
以下命令需要 Redis 7.4 或更新版本,在测试实例的 redis-cli 中执行。文中结果是预期说明,未实际连接 Redis 运行。
redis
DEL tutorial:{hpersist}:profile
HSET tutorial:{hpersist}:profile name Alice temp draft
HEXPIRE tutorial:{hpersist}:profile 300 FIELDS 2 name temp
HTTL tutorial:{hpersist}:profile FIELDS 2 name temp
HPERSIST tutorial:{hpersist}:profile FIELDS 1 temp
HTTL tutorial:{hpersist}:profile FIELDS 2 name temp
HPERSIST tutorial:{hpersist}:profile FIELDS 2 name absent
预期结果:HEXPIRE 返回 [1, 1];第一次 HTTL 返回两个递减的正数;HPERSIST 对 temp 返回 [1];第二次 HTTL 中 temp 变为 -1,name 仍为正数;最后一次 HPERSIST 返回 [1, -2],name 被成功转为永久,absent 因不存在返回 -2。
四、字段 TTL 与 Key TTL 的区别
这是最容易混淆的地方。HPERSIST 只作用于字段级过期时间,不影响整个 Key 的过期时间。
| 操作对象 | 设置命令 | 查询命令 | 移除命令 |
|---|---|---|---|
| Hash 字段 | HEXPIRE、HPEXPIRE、HEXPIREAT、HPEXPIREAT | HTTL、HPTTL、HEXPIRETIME、HPEXPIRETIME | HPERSIST |
| 整个 Key | EXPIRE、PEXPIRE、EXPIREAT、PEXPIREAT | TTL、PTTL、EXPIRETIME、PEXPIRETIME | PERSIST |
redis
DEL tutorial:{hpersist}:two
HSET tutorial:{hpersist}:two a 1
EXPIRE tutorial:{hpersist}:two 600
HEXPIRE tutorial:{hpersist}:two 300 FIELDS 1 a
TTL tutorial:{hpersist}:two
HTTL tutorial:{hpersist}:two FIELDS 1 a
HPERSIST tutorial:{hpersist}:two FIELDS 1 a
TTL tutorial:{hpersist}:two
HTTL tutorial:{hpersist}:two FIELDS 1 a
预期结果:TTL 约为 600,HTTL 约为 300,两者独立存在;HPERSIST 后 HTTL 变为 -1,但 TTL 仍约为 600,说明 Key 级过期时间未受影响。要让整个 Key 永久有效,应使用 PERSIST。
五、边界情况与错误处理
| 场景 | 行为 |
|---|---|
| Key 不存在 | 按输入字段数返回多个 -2 |
| 字段不存在 | 该项返回 -2 |
| 字段存在但无 TTL | 该项返回 -1,属于"无需操作"而非失败 |
| 字段有 TTL | 该项返回 1,TTL 被移除,值保持不变 |
| Key 是 String、List 等非 Hash | 报 WRONGTYPE 错误 |
| numfields 与实际字段数不符 | 报语法错误 |
| 未写 FIELDS 关键字 | 报语法错误 |
HPERSIST 不会改变字段的值,也不会改变 Key 的 TTL。若字段已经到期,它的表现与不存在一致,返回 -2,无法"复活"已删除的数据。
六、客户端示例
前提为已安装 redis-py 且服务端为 Redis 7.4 或更新版本。使用通用接口显式展示 FIELDS 语法。
python
import redis
r = redis.Redis(host="localhost", port=6379, decode_responses=True)
k = "tutorial:{hpersist}:python"
try:
r.delete(k)
r.hset(k, mapping={"a": "A", "b": "B"})
r.execute_command("HEXPIRE", k, 300, "FIELDS", 2, "a", "b")
fields = ["a", "absent"]
result = r.execute_command("HPERSIST", k, "FIELDS", len(fields), *fields)
print(result) # [1, -2]
for name, value in zip(fields, result):
if value == 1:
print(name, "已转为永久")
elif value == -1:
print(name, "本来就是永久")
else:
print(name, "不存在")
print(r.execute_command("HTTL", k, "FIELDS", 2, "a", "b")) # [-1, 约300]
finally:
r.delete(k)
r.close()
七、并发、原子性与适用场景
HPERSIST 对多个字段原子执行,但"先 HTTL 判断,再 HPERSIST"是跨命令流程,中间可能有其他客户端重新设置 TTL 或删除字段。需要严格条件操作时应使用脚本或事务设计。
典型场景:把原本设了短 TTL 的字段在满足某条件后转为长期有效(例如支付完成后把订单缓存字段转永久)、修正误设的过期时间、在数据迁移前冻结字段生命周期。需要注意的是,转为永久意味着该字段将一直占用内存,直到被显式删除或整个 Key 消失;把大量字段转为永久会削弱缓存自动清理的效果,应配合容量规划。
八、练习、排错与总结
练习:新建 tutorial:{hpersist}:exercise,写入 a=1、b=2;用 HEXPIRE ... 300 FIELDS 2 a b 设置字段 TTL;执行 HPERSIST ... FIELDS 2 a absent,预期返回 [1, -2];用 HTTL 确认 a 为 -1、b 仍为正数;最后执行 PERSIST 观察 Key 级 TTL 的变化,理解两级过期的独立性。
排错要点:返回 -1 表示字段本来就永久,不是失败;返回 -2 表示字段或 Key 不存在,可用 HEXISTS 与 EXISTS 区分;unknown command 时确认服务端版本不低于 7.4;HTTL 仍有数值说明命令未作用到该字段,检查字段名拼写与 numfields 是否匹配。清理使用 DEL tutorial:{hpersist}:profile tutorial:{hpersist}:two tutorial:{hpersist}:exercise。速记:7.4 起支持、移除字段 TTL 而非 Key TTL、返回 -2/-1/1、不改变字段值、大量转永久需评估内存。