Redis命令:EXPIRE

Redis EXPIRE 命令详细教程

EXPIRE 用于为 Key 设置以秒为单位的过期时间(生存时间,TTL),到期后 Redis 会自动删除该 Key。它是 Redis 最常用的通用 Key 操作命令之一,广泛应用于缓存、会话管理、限流、分布式锁等场景。

本文基于 Redis 通用 Key 操作介绍 EXPIRE。Redis 命令本身不区分大小写,因此 EXPIRE、expire 和 Expire 的效果相同;文档统一使用大写形式。EXPIRE 自 Redis 1.0 起就可用。

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

一、命令概览

1. 基本语法

redis 复制代码
EXPIRE key seconds [NX | XX | GT | LT]

参数说明:

参数 说明
key 要设置过期时间的 Key
seconds 过期时间,单位为秒,必须是正整数
NX 仅当 Key 没有过期时间时才设置(Not eXists)
XX 仅当 Key 已有过期时间时才设置(eXists)
GT 仅当新 TTL 大于当前 TTL 时才设置(Greater Than),Redis 7.0+
LT 仅当新 TTL 小于当前 TTL 时才设置(Less Than),Redis 7.0+

返回值:

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

2. 最简单的示例

先写入一个字符串值,然后设置 10 秒过期:

redis 复制代码
SET mykey "Hello"
EXPIRE mykey 10

返回:

text 复制代码
(integer) 1

查看剩余生存时间:

redis 复制代码
TTL mykey

返回类似:

text 复制代码
(integer) 8

二、EXPIRE 的基本用法

1. 设置秒级过期时间

redis 复制代码
SET session:user:1001 "logged_in"
EXPIRE session:user:1001 1800

返回 (integer) 1,表示该会话将在 1800 秒(30 分钟)后过期。

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

redis 复制代码
EXPIRE no-such-key 60

返回:

text 复制代码
(integer) 0

Key 不存在时,EXPIRE 返回 0,不会创建 Key。

3. 秒数为 0 的情况

从 Redis 2.6 开始,EXPIRE 的秒数可以为 0,此时 Key 会立即过期(相当于被删除):

redis 复制代码
SET temp "value"
EXPIRE temp 0
EXISTS temp

EXISTS temp 返回 (integer) 0。

4. 负值秒数

传入负值时,Key 也会立即过期:

redis 复制代码
SET temp "value"
EXPIRE temp -1
EXISTS temp

EXISTS temp 返回 (integer) 0。

三、过期时间选项(NX / XX / GT / LT)

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

1. NX --- 仅当没有过期时间时设置

NX(Not eXists):只有当 Key 当前没有关联过期时间(即永久有效)时,才设置新的过期时间。

redis 复制代码
SET permanent-key "forever"
EXPIRE permanent-key 3600 NX

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

如果 Key 已有过期时间:

redis 复制代码
SET temp-key "temp" EX 60
EXPIRE temp-key 3600 NX

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

2. XX --- 仅当已有过期时间时设置

XX(eXists):只有当 Key 当前已有过期时间时,才更新为新的过期时间。

redis 复制代码
SET temp-key "temp" EX 60
EXPIRE temp-key 3600 XX

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

如果 Key 是永久有效的:

redis 复制代码
SET permanent-key "forever"
EXPIRE permanent-key 3600 XX

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

3. GT --- 仅当新 TTL 更大时设置

GT(Greater Than):只有当新的 TTL 大于当前剩余 TTL 时,才更新过期时间。Redis 7.0+ 支持。

redis 复制代码
SET mykey "value" EX 100
EXPIRE mykey 200 GT

返回 (integer) 1,因为 200 > 100,TTL 被延长。

如果新 TTL 更小:

redis 复制代码
SET mykey "value" EX 200
EXPIRE mykey 100 GT

返回 (integer) 0,因为 100 < 200,GT 条件不满足。

4. LT --- 仅当新 TTL 更小时设置

LT(Less Than):只有当新的 TTL 小于当前剩余 TTL 时,才更新过期时间。Redis 7.0+ 支持。

redis 复制代码
SET mykey "value" EX 200
EXPIRE mykey 100 LT

返回 (integer) 1,因为 100 < 200,TTL 被缩短。

如果新 TTL 更大:

redis 复制代码
SET mykey "value" EX 100
EXPIRE mykey 200 LT

返回 (integer) 0,因为 200 > 100,LT 条件不满足。

5. 选项组合

NX、XX、GT、LT 不能任意组合,NX 与 XX 互斥,GT 与 LT 也不能与 NX 同时使用(没有当前 TTL 时无法比较大小)。

四、EXPIRE 与相关命令的区别

1. EXPIRE 与 PEXPIRE

EXPIRE 以秒为单位设置过期时间,PEXPIRE 以毫秒为单位:

redis 复制代码
EXPIRE key 10      # 10 秒后过期
PEXPIRE key 10000  # 10000 毫秒(10 秒)后过期

两者语义完全相同,只是精度不同。需要毫秒级精度时使用 PEXPIRE。

2. EXPIRE 与 EXPIREAT / PEXPIREAT

EXPIRE 设置的是相对时间(从现在起多少秒后过期),EXPIREAT 设置的是绝对时间(Unix 时间戳):

redis 复制代码
EXPIRE key 3600              # 3600 秒后过期
EXPIREAT key 1735689600      # 在指定 Unix 时间戳过期
PEXPIREAT key 1735689600000  # 毫秒级时间戳

需要在指定的绝对时间点过期时使用 EXPIREAT。

3. EXPIRE 与 SET ... EX

SET 命令可以通过 EX 选项在设置值的同时指定过期时间,是原子操作:

redis 复制代码
SET cache:key "value" EX 3600

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

redis 复制代码
SET cache:key "value"
EXPIRE cache:key 3600

推荐优先使用 SET ... EX 形式,因为它是原子的,避免了 SET 成功但 EXPIRE 失败导致 Key 永久有效的风险。

4. EXPIRE 与 PERSIST

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

redis 复制代码
SET temp-key "value" EX 3600
PERSIST temp-key
TTL temp-key

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

EXPIRE 是"添加/修改过期时间",PERSIST 是"移除过期时间"。

5. EXPIRE 与 TTL / PTTL

TTL 以秒为单位返回 Key 的剩余生存时间,PTTL 以毫秒为单位:

状态 TTL 返回值
Key 存在且有过期时间 剩余秒数(正数)
Key 存在但无过期时间 -1
Key 不存在 -2
redis 复制代码
SET mykey "value" EX 100
TTL mykey    # 返回 100 左右
PTTL mykey   # 返回 100000 左右

6. EXPIRE 与 EXPIRETIME / PEXPIRETIME

EXPIRETIME 返回 Key 过期时间对应的 Unix 时间戳(秒),Redis 7.0+ 支持:

redis 复制代码
SET mykey "value" EX 100
EXPIRETIME mykey

返回类似 (integer) 1735689600。

Key 不存在或无过期时间时返回 -1 或 -2(规则同 TTL)。

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

1. 覆盖式更新

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

redis 复制代码
SET mykey "value" EX 100
EXPIRE mykey 200
TTL mykey

TTL mykey 返回约 200,过期时间被重置。

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

使用 SET 命令覆盖 Key 的值时,原有的过期时间会被清除,Key 变为永久有效:

redis 复制代码
SET mykey "old" EX 3600
SET mykey "new"
TTL mykey

TTL mykey 返回 -1。

注意:SET ... EX 或 SET ... PX 形式则会设置新的过期时间。

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

以下命令也会影响 Key 的过期时间:

命令 对过期时间的影响
SET、GETSET 清除过期时间(Key 变为永久有效)
SET ... EX / PX 设置新的过期时间
INCR / DECR / INCRBY 保留原有过期时间
LPUSH / RPUSH 保留原有过期时间
HSET / HMSET 保留原有过期时间
SADD / ZADD 保留原有过期时间
RENAME 源 Key 的过期时间转移到目标 Key
COPY 默认不复制过期时间(目标 Key 永久有效)
PERSIST 移除过期时间
GETEX 可同时修改过期属性

简单来说:修改值但不覆盖整个 Key 的命令(如 INCR、LPUSH、HSET 等)不会改变过期时间;覆盖整个 Key 的命令(如 SET)会清除过期时间。

六、Redis 过期机制原理

1. 惰性过期(Passive Expiration)

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

特点:

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

2. 定期过期(Active Expiration)

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

特点:

  • 限制每次检查的数量和耗时,避免阻塞
  • 不能保证所有过期 Key 都被及时删除
  • 通过随机采样逐步清理过期 Key

3. 内存淘汰(Eviction)

当 Redis 内存使用达到 maxmemory 上限时,会触发内存淘汰策略,可能删除未过期的 Key 以释放内存。

淘汰策略包括 volatile-lru、allkeys-lru、volatile-lfu、allkeys-lfu、volatile-random、allkeys-random、volatile-ttl、noeviction 等。

4. 过期 Key 的复制与 AOF

  • 主从复制 :主节点删除过期 Key 后,会向所有从节点发送 DEL 命令,从节点被动删除。
  • AOF 持久化 :Key 过期被删除时,会向 AOF 文件追加一条 DEL 命令。
  • 从节点不会主动过期 Key,而是等待主节点的 DEL 命令,以保证主从数据一致性。

七、EXPIRE 与不同数据类型

EXPIRE 作用于整个 Key,而不是 Key 内部的单个元素。所有数据类型(String、Hash、List、Set、Sorted Set、Stream 等)的过期行为一致。

redis 复制代码
HSET user:1001 name "Alice" age 30
EXPIRE user:1001 3600
redis 复制代码
RPUSH queue:jobs job-a job-b job-c
EXPIRE queue:jobs 1800
redis 复制代码
SADD tags redis python java
EXPIRE tags 7200
redis 复制代码
ZADD leaderboard 100 "player1" 200 "player2"
EXPIRE leaderboard 86400

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

注意:Redis 7.4 引入了 Hash 字段级过期(HEXPIRE 等),但这是字段级别的,与 Key 级别的 EXPIRE 不同。

八、事务、Pipeline 与并发场景

1. 在事务中使用

EXPIRE 可以放入事务队列:

redis 复制代码
MULTI
SET lock:order:1001 "owner"
EXPIRE lock:order:1001 30
EXEC

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

2. 原子性与 SET ... EX

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

推荐使用原子的 SET ... EX 形式:

redis 复制代码
SET lock:order:1001 "owner" EX 30

这保证了设置值和设置过期时间在同一个原子操作中完成。

3. EXPIRE 与分布式锁

在分布式锁场景中,EXPIRE 用于设置锁的自动释放时间,防止锁持有者崩溃后锁永远无法释放:

redis 复制代码
SET lock:resource "client-id" NX EX 30

这里 NX 保证只有一个客户端能获取锁,EX 30 保证 30 秒后锁自动释放。

仅使用 EXPIRE 来续期锁(锁续期/看门狗机制)时,应使用 XX 选项确保锁仍然存在:

redis 复制代码
EXPIRE lock:resource 30 XX

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

4. Pipeline 批量设置

需要为大量 Key 设置过期时间时,可以使用 Pipeline 批量发送 EXPIRE 命令,减少网络往返。

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

1. redis-cli

bash 复制代码
redis-cli SET session:user "active"
redis-cli EXPIRE session:user 1800
redis-cli TTL session:user
redis-cli PERSIST session:user

2. Python(redis-py)

python 复制代码
import redis

client = redis.Redis(host="localhost", port=6379, decode_responses=True)
client.set("session:user", "active")

# 设置 1800 秒过期
client.expire("session:user", 1800)

# 查看剩余 TTL
ttl = client.ttl("session:user")
print(ttl)  # 1800 左右

# 移除过期时间
client.persist("session:user")
print(client.ttl("session:user"))  # -1

3. Node.js(node-redis)

javascript 复制代码
import { createClient } from "redis";

const client = createClient();
await client.connect();

await client.set("session:user", "active");

// 设置 1800 秒过期
await client.expire("session:user", 1800);

// 查看剩余 TTL
const ttl = await client.ttl("session:user");
console.log(ttl); // 1800 左右

// 移除过期时间
await client.persist("session:user");
console.log(await client.ttl("session:user")); // -1

await client.quit();

4. Java(Jedis)

java 复制代码
try (Jedis jedis = new Jedis("localhost", 6379)) {
    jedis.set("session:user", "active");

    // 设置 1800 秒过期
    long result = jedis.expire("session:user", 1800);
    System.out.println(result); // 1

    // 查看剩余 TTL
    long ttl = jedis.ttl("session:user");
    System.out.println(ttl); // 1800 左右

    // 移除过期时间
    jedis.persist("session:user");
    System.out.println(jedis.ttl("session:user")); // -1
}

十、典型使用场景

1. 缓存管理

为缓存数据设置过期时间,自动失效并刷新:

redis 复制代码
SET cache:product:1001 "{...json...}" EX 3600

缓存数据在 1 小时后自动过期,下次访问时重新从数据库加载。

2. 会话管理

用户登录会话设置过期时间,超时自动登出:

redis 复制代码
SET session:abc123 "user:1001" EX 1800

用户每次操作后续期(刷新 TTL):

redis 复制代码
EXPIRE session:abc123 1800

3. 分布式锁

为锁设置过期时间,防止死锁:

redis 复制代码
SET lock:order:1001 "client-uuid" NX EX 30

锁的持有者崩溃后,30 秒后锁自动释放。

4. 限流计数

滑动窗口限流中,为计数器设置过期时间:

redis 复制代码
INCR rate:limit:user:1001
EXPIRE rate:limit:user:1001 60

每分钟重置一次计数。

5. 一次性令牌

验证码、重置密码链接等一次性数据设置短过期时间:

redis 复制代码
SET sms:code:13800138000 "123456" EX 300

5 分钟后验证码自动失效。

6. 延迟任务

利用 Key 过期事件(keyspace notification)实现简单的延迟任务:

redis 复制代码
SET delay:task:1001 "data" EX 3600

配合 notify-keyspace-events Ex 配置,订阅过期事件来触发延迟任务。

十一、性能与使用建议

  1. 时间复杂度:O(1)。

  2. EXPIRE 是写命令(ACL 类别为 @keyspace、@write、@fast),会修改 Key 的元数据。

  3. 优先使用 SET key value EX seconds 而不是 SET + EXPIRE 两步操作,避免 SET 成功但 EXPIRE 失败导致 Key 永久有效。

  4. 大量 Key 同时过期可能导致 Redis 瞬间压力增大(缓存雪崩)。可以在过期时间上加入随机偏移,打散过期时间:

    redis 复制代码
    EXPIRE cache:key 3600
    # 改为 3600 ± 300 秒的随机值,避免同时过期
  5. 不要对大 Key 使用过短的过期时间(如频繁创建和删除大集合 Key),因为删除大 Key 可能阻塞 Redis。

  6. 需要精确控制过期时间点时使用 EXPIREAT,需要毫秒精度时使用 PEXPIRE/PEXPIREAT。

  7. Redis 7.0+ 的条件选项(NX、XX、GT、LT)可以在很多场景下替代"先 TTL 判断再 EXPIRE"的非原子流程,使用更安全。

  8. 集群模式下,EXPIRE 作用于单个 Key,不受哈希槽限制;涉及多个 Key 的操作需注意槽位分布。

十二、常见问题排查

问题 1:EXPIRE 返回 1 但 Key 没有过期

确认以下几点:

  1. 使用 TTL key 确认剩余时间是否正确。
  2. 是否在 EXPIRE 之后又执行了 SET 等会清除过期时间的命令。
  3. Key 是否被 PERSIST 移除了过期时间。
  4. 是否是从节点读取(从节点不会主动过期 Key,等待主节点同步)。
redis 复制代码
TTL mykey
PTTL mykey
EXPIRETIME mykey

问题 2:Key 提前过期了

可能的原因:

  1. 内存达到 maxmemory 上限,触发了内存淘汰策略,Key 被提前淘汰。
  2. 有其他客户端调用了 DEL、SET(覆盖)等命令删除或覆盖了 Key。
  3. EXPIRE 的秒数计算有误(注意是秒不是毫秒)。
  4. 系统时间被修改(Redis 使用系统时间计算过期,时间跳转会影响过期判断)。

问题 3:EXPIRE 总是返回 0

可能的原因:

  1. Key 不存在(先 EXISTS 确认)。
  2. 使用了条件选项(NX/XX/GT/LT)但条件不满足。
  3. 传入的秒数格式不正确(必须是整数)。

问题 4:过期的 Key 还占用内存

Redis 的过期删除是惰性 + 定期采样的混合策略,已过期但未被访问也未被采样到的 Key 会暂时留在内存中。这是正常现象,内存不会无限增长,因为定期采样会逐步清理。

如果内存压力大,可以:

  • 调整 hz 参数提高定期检查频率(会增加 CPU 消耗)。
  • 启用内存淘汰策略。
  • 主动使用 SCAN + EXPIRE/DEL 批量清理。

问题 5:Redis 重启后过期时间丢失

如果使用 RDB 持久化,Key 的过期信息会保存在 RDB 文件中,重启后恢复。如果使用 AOF 持久化,EXPIRE 命令会被追加到 AOF 文件中,重启后通过重放恢复。

注意:如果 Key 在持久化之前已经过期,RDB/AOF 中不会包含该 Key。

十三、完整练习

下面的示例演示从设置过期时间、验证 TTL、条件选项、更新过期时间,到与 SET、PERSIST、TTL 等命令配合使用的完整流程:

redis 复制代码
DEL demo:key
DEL demo:perm
DEL demo:renew

# 基本用法:设置 60 秒过期
SET demo:key "hello"
EXPIRE demo:key 60
TTL demo:key

# 对不存在的 Key 设置过期
EXPIRE demo:noexist 60

# SET 会清除过期时间
SET demo:key "new value"
TTL demo:key

# 使用 SET ... EX 原子设置
SET demo:key "with ttl" EX 120
TTL demo:key

# PERSIST 移除过期时间
PERSIST demo:key
TTL demo:key

# 条件选项示例(需要 Redis 7.0+)
# NX:仅当无过期时间时设置
SET demo:perm "permanent"
EXPIRE demo:perm 300 NX
TTL demo:perm
EXPIRE demo:perm 600 NX   # 返回 0,因为已有过期时间

# XX:仅当有过期时间时更新
EXPIRE demo:perm 600 XX
TTL demo:perm

# 续期场景模拟
SET demo:renew "session" EX 30
TTL demo:renew
EXPIRE demo:renew 60 XX   # 续期到 60 秒
TTL demo:renew

# Hash 类型的过期
HSET demo:hash name "Alice" age 30
EXPIRE demo:hash 3600
TTL demo:hash

# 验证过期后 Key 不存在
# (先设置极短过期时间,等待后验证)
SET demo:short "gone" EX 1
# 等待 1 秒后执行:
# EXISTS demo:short
# TTL demo:short

预期结果:

  • EXPIRE demo:key 60 返回 (integer) 1,TTL demo:key 返回约 60。
  • EXPIRE demo:noexist 60 返回 (integer) 0。
  • SET demo:key "new value" 后 TTL demo:key 返回 -1(过期时间被清除)。
  • SET demo:key "with ttl" EX 120 后 TTL demo:key 返回约 120。
  • PERSIST demo:key 返回 1,TTL demo:key 返回 -1。
  • EXPIRE demo:perm 300 NX 返回 1,第二次 NX 返回 0。
  • EXPIRE demo:perm 600 XX 返回 1,TTL 返回约 600。
  • EXPIRE demo:renew 60 XX 返回 1,TTL 从约 30 变为约 60。
  • EXPIRE demo:hash 3600 返回 1,TTL demo:hash 返回约 3600。
  • 过期后 EXISTS demo:short 返回 (integer) 0。

十四、命令速查表

需求 命令 精度
设置秒级过期时间 EXPIRE key seconds 秒
设置毫秒级过期时间 PEXPIRE key milliseconds 毫秒
按 Unix 时间戳设置过期(秒) EXPIREAT key timestamp 秒
按 Unix 时间戳设置过期(毫秒) PEXPIREAT key milliseconds-timestamp 毫秒
获取剩余生存时间(秒) TTL key 秒
获取剩余生存时间(毫秒) PTTL key 毫秒
获取过期时间戳(秒) EXPIRETIME key 秒
获取过期时间戳(毫秒) PEXPIRETIME key 毫秒
移除过期时间(永久有效) PERSIST key ---
原子设置值和过期时间 SET key value EX seconds 秒
仅当无过期时间时设置 EXPIRE key seconds NX 秒
仅当有过期时间时更新 EXPIRE key seconds XX 秒
仅当新 TTL 更大时更新 EXPIRE key seconds GT 秒
仅当新 TTL 更小时更新 EXPIRE key seconds LT 秒

总结

EXPIRE 的核心作用是为 Key 设置以秒为单位的过期时间,到期后 Redis 自动删除该 Key:

redis 复制代码
EXPIRE key seconds [NX | XX | GT | LT]

使用时重点注意五点:

  • 优先使用 SET key value EX seconds 的原子形式,避免 SET 成功但 EXPIRE 失败导致 Key 永久有效。
  • SET 等覆盖 Key 的命令会清除原有过期时间;INCR、LPUSH、HSET 等修改内部数据的命令则保留过期时间。
  • Redis 通过惰性过期 + 定期采样的方式删除过期 Key,不能保证过期后立即释放内存。
  • Redis 7.0+ 支持 NX、XX、GT、LT 条件选项,可以原子地实现"仅当无过期时设置"、"续期"等场景。
  • 大量 Key 同时过期可能引发缓存雪崩,建议在过期时间上加入随机偏移以打散过期峰值。
相关推荐
ly76894 小时前
Redis 内存优化与性能调优:编码转换、内存碎片与慢查询的定位链路
redis·性能调优·内存优化·慢查询·编码转换
我不会起名字3227 小时前
Redis 入门(三):Spring Boot 整合 Redis,RedisTemplate 实战与序列化乱码
redis·spring·工具类·jedis·lettuce
神秘的猪头8 小时前
Redis 数据结构实战指南:从缓存、点赞、排行榜到消息队列
redis
新鲜势力呀10 小时前
PHP 消息队列实战:从同步接口阻塞到 RabbitMQ + Redis队列 + 异步任务架构完整优化方案
redis·rabbitmq·php
for_ever_love__11 小时前
Redis 持久化讲透:RDB、AOF 与混合持久化怎么选
java·数据库·redis·持久化·aof·区别·rdb
拾贰_C12 小时前
【English | conversation 】call | 抖音AI短句情景:打电话---come over
数据库·redis·缓存
ShineWinsu16 小时前
对于Redis:缓存的解析
java·redis·缓存·缓存穿透·缓存击穿·缓存雪崩·缓存预热
害人终害己16 小时前
redis修改密码的地方在哪里
数据库·redis·缓存
我不会起名字32216 小时前
Redis 入门(一):Redis 是什么,为什么后端都离不开它
linux·redis·docker·安装·cli
for_ever_love__17 小时前
缓存穿透、击穿、雪崩:三个经典问题与完整解决方案
java·数据库·redis·缓存·哈希算法·布隆过滤器·雪崩