Redis命令:HPEXPIREAT

Redis HPEXPIREAT 命令详细教程

HPEXPIREAT 为 Hash 中一个或多个字段设置绝对到期时刻,时间以 Unix 毫秒级时间戳表示。它与 HEXPIREAT 语义相同,只是精度为毫秒。

资料合集:https://pan.quark.cn/s/10e98d308913、https://pan.quark.cn/s/f56bc69c5338

一、概览与语法

redis 复制代码
HPEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT] FIELDS numfields field [field ...]
项目 说明
数据类型 Hash 的字段级过期时间
支持版本 Redis 7.4.0 起
key 一个 Hash Key
unix-time-milliseconds 绝对 Unix 时间戳,单位毫秒
条件选项 NX、XX、GT、LT 四者最多选一,互斥
FIELDS 必填关键字
numfields 字段数量,必须与后续字段参数个数一致
时间复杂度 O(N),N 为指定字段数量
ACL @write、@hash、@fast
命令标记 write、denyoom、fast

官方明确说明:过去的时间戳会立即删除该字段。$TRAE_REF

二、逐字段返回值

数值 含义
-2 该字段不存在,或整个 Key 不存在
0 指定的 NX、XX、GT、LT 条件未满足
1 过期时间已设置或更新
2 传入的是过去的 Unix 时间,字段被立即删除

官方对 2 的定义覆盖了四种命令:HEXPIRE 或 HPEXPIRE 以 0 秒/毫秒调用,HEXPIREAT 或 HPEXPIREAT 以过去的 Unix 时间调用。$TRAE_REF

三、基础示例

以下命令需要 Redis 7.4 或更新版本,在测试实例的 redis-cli 中执行。文中结果是预期说明,未实际连接 Redis 运行。官方示例使用的 1715704971000 是一个毫秒级时间戳。

redis 复制代码
DEL tutorial:{hpexpireat}:profile
HSET tutorial:{hpexpireat}:profile field1 hello field2 world
HPEXPIREAT tutorial:{hpexpireat}:profile 1715704971000 FIELDS 2 field1 field2
HPTTL tutorial:{hpexpireat}:profile FIELDS 2 field1 field2
HPEXPIRETIME tutorial:{hpexpireat}:profile FIELDS 2 field1 field2
HPEXPIREAT tutorial:{hpexpireat}:profile 1 FIELDS 1 field1
HEXISTS tutorial:{hpexpireat}:profile field1
HEXISTS tutorial:{hpexpireat}:profile field2

预期结果:HPEXPIREAT 返回 [1, 1];HPTTL 返回两个相同的毫秒值(官方示例中为 303340 这类数值,取决于执行时刻与目标时间戳的差值);HPEXPIRETIME 返回两个相同的毫秒时间戳。传入过去的时间戳 1 后返回 [2],field1 被立即删除,HEXISTS 返回 0,而 field2 仍为 1。

如果示例中的时间戳已经过去,第一次调用就会返回 [2, 2] 并删除两个字段。实际使用时请替换为未来的时间戳。

四、如何生成毫秒时间戳

毫秒时间戳由客户端生成,Redis 不提供换算命令。

bash 复制代码
date +%s%3N
python 复制代码
import time
from datetime import datetime, timedelta, timezone

now_ms = int(time.time() * 1000)
print(now_ms)                                              # 当前毫秒时间戳
print(now_ms + 60_000)                                     # 一分钟后
print(int((datetime.now(timezone.utc) + timedelta(minutes=5)).timestamp() * 1000))

常见错误是把秒级时间戳当作毫秒传入:秒级数值比毫秒小三个数量级,作为毫秒时间戳几乎必然是"过去的时间",会导致字段被立即删除。反过来,把毫秒时间戳传给 HEXPIREAT 则会得到一个极其遥远的未来时刻。换算时务必确认单位。

五、条件选项与边界情况

选项 判断条件
NX 字段当前没有过期时间时才设置
XX 字段当前已有过期时间时才设置
GT 新到期时刻严格晚于当前到期时刻时才设置
LT 新到期时刻严格早于当前到期时刻时才设置

官方说明:对于 GT 与 LT,没有过期时间的字段被视为无限大的 TTL,因此有限时间的 GT 对无 TTL 字段通常不成立,LT 会成立。$TRAE_REF

场景 行为
Key 不存在 按输入字段数返回多个 -2,不创建 Key
字段不存在 该项返回 -2
时间戳已过去 该字段被立即删除,返回 2
时间戳不是整数 报参数类型错误
Key 是 String、List 等非 Hash 报 WRONGTYPE 错误
numfields 与实际字段数不符 报语法错误

六、与相关命令的对照

命令 对象 时间表达 单位
HPEXPIRE Hash 字段 相对 毫秒
HPEXPIREAT Hash 字段 绝对 毫秒时间戳
HEXPIREAT Hash 字段 绝对 秒时间戳
PEXPIREAT 整个 Key 绝对 毫秒时间戳
HPEXPIRETIME Hash 字段 查询绝对时刻 毫秒时间戳

字段级过期与 Key 级过期相互独立,谁先到期谁先限制数据可用性。查询字段绝对到期时刻使用 HPEXPIRETIME,查询剩余时间使用 HPTTL。

七、Python 客户端示例

前提为已安装 redis-py 且服务端为 Redis 7.4 或更新版本。

python 复制代码
import time
import redis

r = redis.Redis(host="localhost", port=6379, decode_responses=True)
k = "tutorial:{hpexpireat}:python"
try:
    r.delete(k)
    r.hset(k, mapping={"a": "A", "b": "B"})

    fields = ["a", "b", "missing"]
    deadline_ms = int(time.time() * 1000) + 60_000
    result = r.execute_command(
        "HPEXPIREAT", k, deadline_ms, "FIELDS", len(fields), *fields
    )
    print(result)   # [1, 1, -2]

    print(r.execute_command("HPEXPIRETIME", k, "FIELDS", 2, "a", "b"))
    # [deadline_ms, deadline_ms]

    print(r.execute_command("HPEXPIREAT", k, 1, "FIELDS", 1, "a"))   # [2]
    print(r.hexists(k, "a"))   # False
finally:
    r.delete(k)
    r.close()

八、并发、重试与适用场景

绝对时间戳的明显优势是重试语义稳定:把同一请求原样重发,到期时刻不会因为重试时刻变化而延长,这一点优于相对 TTL。但重试仍可能重复执行,条件选项只按各自规则判断,不是通用幂等保证;请求超时也不代表未执行,应先查询 HPEXPIRETIME 或 HEXISTS 再决定是否重发。

单条 HPEXPIREAT 对多个字段原子执行,但"先 HSET 写值,再 HPEXPIREAT 设期限"是两条独立调用,中间可能有其他写入。需要两者一起生效时应使用经过验证的脚本或事务设计。

典型场景:与外部系统对齐的固定过期时刻、活动截止时间、定时失效的权益字段。由于毫秒时间戳依赖客户端时钟,而判定在服务端进行,两者存在偏差时实际存活时长会与预期不符,跨机房场景应统一时间源并留出余量。

九、练习、排错与总结

练习:新建 tutorial:{hpexpireat}:exercise,写入 a=1、b=2;用 Python 计算当前时间加 10 分钟的毫秒时间戳,执行 HPEXPIREAT ... <ts> FIELDS 2 a b,预期返回 [1, 1];用 HPEXPIRETIME 确认返回该时间戳;再用 1 无条件调用,预期返回 [2] 且字段被删除。

排错要点:unknown command 时确认服务端版本不低于 7.4;字段被意外立即删除时,检查传入的是秒级还是毫秒级时间戳;返回 0 检查条件选项;返回 -2 检查字段是否缺失;HPEXPIRETIME 返回 -1 表示字段永久有效。清理使用 DEL tutorial:{hpexpireat}:profile tutorial:{hpexpireat}:exercise。速记:7.4 起支持、绝对毫秒时间戳、过去时间戳立即删除、FIELDS 必填、返回 -2/0/1/2、注意单位换算。

相关推荐
code2cat1 小时前
【随笔】MCP缓存期限与共享范围:让Agent复用资料时记住边界
java·后端·缓存·ai agent·mcp
吴声子夜歌3 小时前
Nginx应用与运维——Nginx缓存服务应用实战(一)
运维·nginx·缓存
ShineWinsu4 小时前
对于Redis:ZSet类型的解析
数据库·c++·redis·缓存·面试·有序集合·zset
新鲜势力呀4 小时前
PHP 定时任务系统实战:从 Cron 混乱执行到任务调度中心 + Redis队列 + 失败重试
android·java·redis
hweiyu005 小时前
Redis命令:HPTTL
redis·缓存
imDwAaY5 小时前
Redis List 是链表吗?从 Ziplist 到 Quicklist 揭开底层实现
redis·后端
知守观5 小时前
从三个带病的 Guava 本地缓存出发:Redis + Guava 二级缓存的读写路径与失效设计推演
java·redis·后端
imDwAaY5 小时前
Redis Set 如何节省内存?从整数集合到哈希表的设计取舍
数据结构·redis·散列表