当前位置: 代码网 > it编程>数据库>Redis > Redis中EXPIREAT命令实现

Redis中EXPIREAT命令实现

2026年08月28日 Redis 我要评论
expireat 用于为 key 设置以 unix 时间戳(秒)为单位的绝对过期时间,到期后 redis 会自动删除该 key。与 expire 设置相对时间(从现在起多少秒后过期)不同,expire

expireat 用于为 key 设置以 unix 时间戳(秒)为单位的绝对过期时间,到期后 redis 会自动删除该 key。与 expire 设置相对时间(从现在起多少秒后过期)不同,expireat 指定的是一个绝对时间点,适用于需要在特定日期或时刻让 key 失效的场景。

本文基于 redis 通用 key 操作介绍 expireat。redis 命令本身不区分大小写,因此 expireat、expireat 和 expireat 的效果相同;文档统一使用大写形式。expireat 自 redis 1.2 起可用,条件选项 nx、xx、gt、lt 从 redis 7.0 开始支持。

一、命令概览

1. 基本语法

expireat key timestamp [nx | xx | gt | lt]

参数说明:

参数说明
key要设置过期时间的 key
timestampunix 时间戳(秒),绝对过期时间点
nx仅当 key 没有过期时间时才设置(not exists),redis 7.0+
xx仅当 key 已有过期时间时才设置(exists),redis 7.0+
gt仅当新过期时间戳大于当前过期时间戳时才设置(greater than),redis 7.0+
lt仅当新过期时间戳小于当前过期时间戳时才设置(less than),redis 7.0+

返回值:

  • 设置成功返回 (integer) 1。
  • 设置失败返回 (integer) 0(如 key 不存在、条件不满足等)。

2. 最简单的示例

先写入一个字符串值,然后设置在指定 unix 时间戳过期:

set mykey "hello"
expireat mykey 1735689600

返回:

(integer) 1

查看剩余生存时间:

ttl mykey

返回值为当前时间到 1735689600 的剩余秒数(正数)。

查看过期时间戳:

expiretime mykey

返回:

(integer) 1735689600

二、unix 时间戳基础

1. 什么是 unix 时间戳

unix 时间戳是从 utc 1970 年 1 月 1 日 00:00:00 起经过的秒数。expireat 接受的就是这种秒级 unix 时间戳。

常见时间点的 unix 时间戳(示例):

时间(utc)unix 时间戳
2025-01-01 00:00:001735689600
2025-06-01 00:00:001748736000
2026-01-01 00:00:001767225600
2026-08-28 00:00:001787548800

2. 获取当前 unix 时间戳

在 redis-cli 中可以用 time 命令获取服务器当前时间:

time

返回类似:

1) "1724719123"
2) "456789"

第一个值为秒级时间戳,第二个值为微秒部分。

在 shell 中获取:

date +%s

3. 时间戳计算

可以结合 shell 命令计算未来的时间戳。例如设置 1 小时后过期:

redis-cli expireat mykey $(( $(date +%s) + 3600 ))

设置明天同一时间过期:

redis-cli expireat mykey $(( $(date +%s) + 86400 ))

三、expireat 的基本用法

1. 设置绝对过期时间

set promo:coupon:1001 "discount50"
expireat promo:coupon:1001 1767225600

返回 (integer) 1,该 key 将在 utc 2026-01-01 00:00:00 过期。

2. 对不存在的 key 设置过期

expireat no-such-key 1735689600

返回:

(integer) 0

key 不存在时,expireat 返回 0,不会创建 key。

3. 时间戳在过去(立即过期)

传入的时间戳小于当前时间戳时,key 会立即过期(相当于被删除):

set temp "value"
expireat temp 1
exists temp

exists temp 返回 (integer) 0。

利用这一特性可以实现"立即删除"的效果,但通常应使用 del 或 unlink。

4. 时间戳为 0

unix 时间戳 0 对应 1970-01-01 00:00:00 utc,显然在过去,key 会立即过期:

set temp "value"
expireat temp 0
exists temp

exists temp 返回 (integer) 0。

5. 负值时间戳

传入负值时间戳时,key 也会立即过期:

set temp "value"
expireat temp -1
exists temp

exists temp 返回 (integer) 0。

四、条件选项(nx / xx / gt / lt)

redis 7.0 及以上版本支持条件选项,用于更精细地控制过期时间的设置行为。

1. nx — 仅当没有过期时间时设置

nx(not exists):只有当 key 当前没有关联过期时间(即永久有效)时,才设置新的过期时间。

set permanent-key "forever"
expireat permanent-key 1735689600 nx

返回 (integer) 1,因为该 key 原本没有过期时间。

如果 key 已有过期时间:

set temp-key "temp" ex 60
expireat temp-key 1735689600 nx

返回 (integer) 0,因为 nx 条件不满足。

2. xx — 仅当已有过期时间时设置

xx(exists):只有当 key 当前已有过期时间时,才更新为新的过期时间。

set temp-key "temp" ex 60
expireat temp-key 1735689600 xx

返回 (integer) 1,因为该 key 已有过期时间,成功更新。

如果 key 是永久有效的:

set permanent-key "forever"
expireat permanent-key 1735689600 xx

返回 (integer) 0,因为 xx 条件不满足。

3. gt — 仅当新过期时间更晚时设置

gt(greater than):只有当新的过期时间戳大于当前剩余过期时间戳时,才更新过期时间。redis 7.0+ 支持。

set mykey "value" ex 100
expireat mykey 1735689600 gt

如果 1735689600 对应的过期时间在当前过期时间之后,返回 (integer) 1,过期时间被延长。

如果新过期时间更早:

set mykey "value" exat 1735689600
expireat mykey 1000 gt

时间戳 1000 对应 1970-01-01,远在当前过期时间之前,返回 (integer) 0。

4. lt — 仅当新过期时间更早时设置

lt(less than):只有当新的过期时间戳小于当前剩余过期时间戳时,才更新过期时间。redis 7.0+ 支持。

set mykey "value" exat 1735689600
expireat mykey 1735660000 lt

如果 1735660000 < 1735689600,返回 (integer) 1,过期时间被缩短。

如果新过期时间更晚:

set mykey "value" ex 100
expireat mykey 1735689600 lt

如果 1735689600 对应的过期时间在当前过期时间之后,返回 (integer) 0。

5. 选项约束

nx 与 xx 互斥,gt 与 lt 互斥。gt 和 lt 不能与 nx 同时使用(没有当前过期时间时无法比较大小)。

五、expireat 与相关命令的区别

1. expireat 与 expire

expireat 设置绝对过期时间(unix 时间戳),expire 设置相对过期时间(从现在起的秒数):

expire key 3600              # 3600 秒后过期
expireat key 1735689600      # 在指定 unix 时间戳过期

两者可以互相转换:expireat key timestamp 等价于 expire key (timestamp - 当前时间戳)。

需要在特定时间点过期时用 expireat,需要"多少秒后"过期时用 expire。

2. expireat 与 pexpireat

expireat 接受秒级时间戳,pexpireat 接受毫秒级时间戳:

expireat key 1735689600          # 秒级时间戳
pexpireat key 1735689600000      # 毫秒级时间戳

两者语义相同,精度不同。需要毫秒级精度时使用 pexpireat。

3. expireat 与 set … exat

set 命令(redis 6.2+)可以通过 exat 选项在设置值的同时指定绝对过期时间戳,是原子操作:

set cache:key "value" exat 1735689600

等效于(但更高效且原子):

set cache:key "value"
expireat cache:key 1735689600

推荐优先使用 set ... exat 形式,避免 set 成功但 expireat 失败导致 key 永久有效的风险。

4. expireat 与 expiretime / pexpiretime

expiretime 返回 key 过期时间对应的 unix 时间戳(秒),redis 7.0+ 支持:

set mykey "value" exat 1735689600
expiretime mykey

返回:

(integer) 1735689600
状态expiretime 返回值
key 存在且有过期时间过期时间戳(正数)
key 存在但无过期时间-1
key 不存在-2

5. expireat 与 persist

persist 用于移除 key 的过期时间,使其变为永久有效:

set temp-key "value" exat 1735689600
persist temp-key
ttl temp-key

ttl temp-key 返回 -1(永久有效)。

6. 相关命令总览

命令时间类型精度说明
expire key seconds相对多少秒后过期
pexpire key ms相对毫秒多少毫秒后过期
expireat key timestamp绝对在指定时间戳过期
pexpireat key timestamp绝对毫秒在指定毫秒时间戳过期
ttl key剩余秒数
pttl key毫秒剩余毫秒数
expiretime key过期时间戳(秒)
pexpiretime key毫秒过期时间戳(毫秒)
persist key移除过期时间

六、过期时间的刷新与更新

1. 覆盖式更新

对已有过期时间的 key 再次执行 expireat,会覆盖原有的过期时间:

set mykey "value" exat 1735689600
expireat mykey 1767225600
expiretime mykey

expiretime mykey 返回 1767225600,过期时间被重置。

2. set 命令会清除过期时间

使用 set 命令覆盖 key 的值时(不带 exat/pxat/ex/px 选项),原有的过期时间会被清除,key 变为永久有效:

set mykey "old" exat 1735689600
set mykey "new"
ttl mykey

ttl mykey 返回 -1。

3. 其他会影响过期时间的命令

命令对过期时间的影响
set、getset清除过期时间(key 变为永久有效)
set ... exat / pxat设置新的绝对过期时间
set ... ex / px设置新的相对过期时间
incr / decr / incrby保留原有过期时间
lpush / rpush保留原有过期时间
hset / hmset保留原有过期时间
sadd / zadd保留原有过期时间
rename源 key 的过期时间转移到目标 key
copy默认不复制过期时间
persist移除过期时间
getex可同时修改过期属性

七、redis 过期机制原理

1. 惰性过期(passive expiration)

当客户端访问某个 key 时,redis 会检查该 key 是否已过期,如果已过期则立即删除并返回不存在。

特点:

  • 对 cpu 友好,只在访问时检查
  • 对内存不友好,已过期但未被访问的 key 会一直占用内存

2. 定期过期(active expiration)

redis 会周期性地(默认每秒 10 次)随机抽取一部分设置了过期时间的 key 进行检查,删除已过期的 key。

特点:

  • 限制每次检查的数量和耗时,避免阻塞
  • 通过随机采样逐步清理过期 key

3. 过期 key 的复制与持久化

  • 主从复制:主节点删除过期 key 后,会向所有从节点发送 del 命令。从节点不会主动过期 key。
  • aof 持久化:key 过期被删除时,会向 aof 文件追加一条 del 命令。
  • rdb 持久化:过期 key 不会被写入 rdb 文件。

八、expireat 与不同数据类型

expireat 作用于整个 key,所有数据类型(string、hash、list、set、sorted set、stream 等)的过期行为一致。

hset user:1001 name "alice" age 30
expireat user:1001 1735689600
rpush queue:jobs job-a job-b job-c
expireat queue:jobs 1735689600
sadd tags redis python java
expireat tags 1735689600
zadd leaderboard 100 "player1" 200 "player2"
expireat leaderboard 1735689600

过期后整个 key 及其所有数据都会被删除。

九、事务、pipeline 与并发场景

1. 在事务中使用

expireat 可以放入事务队列:

multi
set promo:banner "summer-sale"
expireat promo:banner 1735689600
exec

exec 执行后返回各命令的结果。

2. 原子性与 set … exat

“先 set 再 expireat” 不是原子操作。如果 set 成功后客户端崩溃或网络中断,expireat 可能不会执行,导致 key 永久有效。

推荐使用原子的 set ... exat 形式(redis 6.2+):

set promo:banner "summer-sale" exat 1735689600

3. 分布式锁续期

使用 expireat 续期分布式锁时,应使用 xx 选项确保锁仍然存在:

expireat lock:resource 1735689600 xx

如果返回 0,说明锁已经不存在(可能已过期或被释放)。

4. pipeline 批量设置

需要为大量 key 设置同一过期时间点时,可以使用 pipeline 批量发送 expireat 命令,减少网络往返。

十、在常见客户端中的使用方式

1. redis-cli

redis-cli set promo:key "summer-sale"
redis-cli expireat promo:key 1735689600
redis-cli expiretime promo.key

结合 shell 动态计算时间戳:

# 设置明天同一时间过期
redis-cli expireat mykey $(( $(date +%s) + 86400 ))

# 设置 2025-06-01 00:00:00 utc 过期
redis-cli expireat mykey $(date -d "2025-06-01 00:00:00 utc" +%s)

2. python(redis-py)

import redis
import time

client = redis.redis(host="localhost", port=6379, decode_responses=true)
client.set("promo:key", "summer-sale")

# 设置在指定 unix 时间戳过期
expire_at = int(time.time()) + 3600  # 1 小时后
client.expireat("promo:key", expire_at)

# 使用 datetime 对象(redis-py 支持)
from datetime import datetime, timezone, timedelta
expire_dt = datetime(2025, 6, 1, 0, 0, 0, tzinfo=timezone.utc)
client.expireat("promo:key", expire_dt)

# 查看过期时间戳
expire_time = client.expiretime("promo:key")
print(expire_time)

3. node.js(node-redis)

import { createclient } from "redis";
const client = createclient();
await client.connect();
await client.set("promo:key", "summer-sale");
// 设置在指定 unix 时间戳过期
const expireat = math.floor(date.now() / 1000) + 3600; // 1 小时后
await client.expireat("promo:key", expireat);
// 查看过期时间戳
const expiretime = await client.expiretime("promo:key");
console.log(expiretime);
await client.quit();

4. java(jedis)

import java.time.instant;
import redis.clients.jedis.jedis;

try (jedis jedis = new jedis("localhost", 6379)) {
    jedis.set("promo:key", "summer-sale");

    // 设置在指定 unix 时间戳过期
    long expireat = instant.now().getepochsecond() + 3600; // 1 小时后
    long result = jedis.expireat("promo:key", expireat);
    system.out.println(result); // 1

    // 使用 date 对象
    import java.util.date;
    jedis.expireat("promo:key", date.from(instant.parse("2025-06-01t00:00:00z")));

    // 查看过期时间戳
    long expiretime = jedis.expiretime("promo:key");
    system.out.println(expiretime);
}

十一、典型使用场景

1. 限时促销活动

促销活动在指定日期结束,自动清理相关 key:

set promo:summer:2025 "active"
expireat promo:summer:2025 1751328000    # 2025-07-01 00:00:00 utc

活动到期后 key 自动失效,无需手动清理。

2. 定时任务触发

利用 key 过期事件(keyspace notification)在指定时间触发任务:

set task:send-email:1001 "data" exat 1735689600

配合 notify-keyspace-events ex 配置,订阅过期事件来触发定时任务。

3. 限时验证码

验证码在特定时间点失效:

# 设置验证码在当天 23:59:59 过期
redis-cli set sms:code:13800138000 "123456"
redis-cli expireat sms:code:13800138000 $(date -d "today 23:59:59" +%s)

4. 会话固定时间失效

用户会话在当天结束时间点自动失效:

redis-cli set session:abc123 "user:1001"
redis-cli expireat session:abc123 $(date -d "tomorrow 00:00:00" +%s)

5. 倒计时活动

秒杀活动在特定时刻开始或结束:

set seckill:stock "100"
expireat seckill:stock 1735689600    # 活动结束时间

6. 数据归档

日志或统计数据在指定日期后自动清理:

set log:2025-01:access "count_data"
expireat log:2025-01:access 1767225600    # 2026-01-01 自动清理 2025 年 1 月数据

十二、性能与使用建议

  1. 时间复杂度:o(1)。
  2. expireat 是写命令(acl 类别为 @keyspace、@write、@fast),会修改 key 的元数据。
  3. 优先使用 set key value exat timestamp 的原子形式,避免 set 成功但 expireat 失败导致 key 永久有效。
  4. 注意时区问题:expireat 接受的是 utc unix 时间戳,不是本地时间。使用 date 命令时注意时区转换。
  5. 大量 key 在同一时间戳过期可能导致 redis 瞬间压力增大(缓存雪崩)。可以在过期时间上加入随机偏移,打散过期时间。
  6. 时间戳在过去时 key 会立即过期,等效于删除。如需删除 key,优先使用 del 或 unlink。
  7. redis 7.0+ 的条件选项(nx、xx、gt、lt)可以在很多场景下替代"先 ttl 判断再设置"的非原子流程,使用更安全。
  8. 集群模式下,expireat 作用于单个 key,不受哈希槽限制。
  9. 服务器时间偏差会影响过期判断。确保 redis 服务器系统时间准确,避免时间跳转导致 key 提前或延后过期。

十三、常见问题排查

问题 1:expireat 返回 1 但 key 立即过期

可能的原因:

  1. 传入的时间戳小于当前时间戳(在过去),key 会立即过期。
  2. 系统时间不准确,导致时间戳判断异常。
  3. 传入的是毫秒时间戳(而非秒级时间戳),导致值远大于预期,但如果误传小毫秒值则可能已过期。
time
expiretime mykey
ttl mykey

问题 2:expireat 返回 0

可能的原因:

  1. key 不存在(先 exists 确认)。
  2. 使用了条件选项(nx/xx/gt/lt)但条件不满足。
  3. 时间戳格式不正确(必须是整数)。

问题 3:key 没有在预期时间过期

可能的原因:

  1. 时区计算错误:expireat 使用 utc 时间戳,而用户可能误用了本地时间。
  2. 系统时间被修改或存在偏差。
  3. 在 expireat 之后又执行了 set 等会清除过期时间的命令。
  4. key 被 persist 移除了过期时间。
  5. 内存达到 maxmemory 上限,触发了内存淘汰策略,key 被提前淘汰。

问题 4:误用毫秒时间戳

expireat 接受秒级时间戳,pexpireat 接受毫秒级时间戳。如果误将毫秒时间戳传给 expireat,会导致过期时间远超预期(大约 1000 倍)。

# 错误:传入了毫秒时间戳
expireat key 1735689600000    # 实际过期时间约为 56670 年后

# 正确:秒级时间戳
expireat key 1735689600

# 如果需要毫秒精度,使用 pexpireat
pexpireat key 1735689600000

问题 5:从节点数据不一致

从节点不会主动过期 key,而是等待主节点的 del 命令。如果从节点读取到"应该已过期"的 key,这是正常行为。确保读取策略与业务需求匹配。

十四、完整练习

下面的示例演示从设置绝对过期时间、验证 ttl 和 expiretime、条件选项、更新过期时间,到与 set ... exat、persist 等命令配合使用的完整流程:

del demo:key
del demo:perm
del demo:renew

# 获取当前时间
time

# 基本用法:设置绝对过期时间
set demo:key "hello"
expireat demo:key 1767225600
expiretime demo:key
ttl demo:key

# 对不存在的 key 设置过期
expireat demo:noexist 1767225600

# set 会清除过期时间
set demo:key "new value"
ttl demo:key

# 使用 set ... exat 原子设置
set demo:key "with ttl" exat 1767225600
expiretime demo:key

# persist 移除过期时间
persist demo:key
ttl demo:key

# 条件选项示例(需要 redis 7.0+)
# nx:仅当无过期时间时设置
set demo:perm "permanent"
expireat demo:perm 1767225600 nx
expiretime demo:perm
expireat demo:perm 1735689600 nx   # 返回 0,因为已有过期时间

# xx:仅当有过期时间时更新
expireat demo:perm 1735689600 xx
expiretime demo:perm

# 续期场景模拟
set demo:renew "session" exat 1735689600
expiretime demo:renew
expireat demo:renew 1767225600 xx   # 续期到更晚的时间
expiretime demo:renew

# hash 类型的过期
hset demo:hash name "alice" age 30
expireat demo:hash 1767225600
expiretime demo:hash

# 验证过去时间戳立即过期
set demo:past "gone"
expireat demo:past 1
exists demo:past

预期结果:

  • expireat demo:key 1767225600 返回 (integer) 1,expiretime demo:key 返回 1767225600,ttl 返回正数。
  • expireat demo:noexist 1767225600 返回 (integer) 0。
  • set demo:key "new value" 后 ttl demo:key 返回 -1。
  • set demo:key "with ttl" exat 1767225600 后 expiretime demo:key 返回 1767225600。
  • persist demo:key 返回 1,ttl demo:key 返回 -1。
  • expireat demo:perm 1767225600 nx 返回 1,第二次 nx 返回 0。
  • expireat demo:perm 1735689600 xx 返回 1,expiretime 返回 1735689600。
  • expireat demo:renew 1767225600 xx 返回 1,expiretime 返回 1767225600。
  • expireat demo:hash 1767225600 返回 1,expiretime demo:hash 返回 1767225600。
  • expireat demo:past 1 后 exists demo:past 返回 (integer) 0。

十五、命令速查表

需求命令时间类型精度
在指定 unix 时间戳过期(秒)expireat key timestamp绝对
在指定 unix 时间戳过期(毫秒)pexpireat key timestamp绝对毫秒
多少秒后过期expire key seconds相对
多少毫秒后过期pexpire key ms相对毫秒
原子设置值和绝对过期时间set key value exat timestamp绝对
原子设置值和绝对过期时间set key value pxat timestamp绝对毫秒
获取剩余生存时间(秒)ttl key
获取剩余生存时间(毫秒)pttl key毫秒
获取过期时间戳(秒)expiretime key
获取过期时间戳(毫秒)pexpiretime key毫秒
移除过期时间persist key
仅当无过期时间时设置expireat key timestamp nx绝对
仅当有过期时间时更新expireat key timestamp xx绝对
仅当新过期时间更晚时设置expireat key timestamp gt绝对
仅当新过期时间更早时设置expireat key timestamp lt绝对

总结

expireat 的核心作用是为 key 设置以秒级 unix 时间戳为单位的绝对过期时间,到期后 redis 自动删除该 key:

expireat key timestamp [nx | xx | gt | lt]

使用时重点注意五点:

  • expireat 使用 utc 秒级 unix 时间戳,注意时区转换和精度(秒而非毫秒)。
  • 时间戳在过去时 key 会立即过期,等效于删除。
  • 优先使用 set key value exat timestamp 的原子形式,避免 set 成功但 expireat 失败。
  • set 等覆盖 key 的命令会清除原有过期时间;incr、lpush、hset 等修改内部数据的命令则保留过期时间。
  • redis 7.0+ 支持 nx、xx、gt、lt 条件选项,可以原子地实现"仅当无过期时设置"、"续期"等场景。

到此这篇关于redis中expireat命令实现的文章就介绍到这了,更多相关redis expireat命令内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com