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 配置,订阅过期事件来触发延迟任务。
十一、性能与使用建议
-
时间复杂度:O(1)。
-
EXPIRE是写命令(ACL 类别为@keyspace、@write、@fast),会修改 Key 的元数据。 -
优先使用
SET key value EX seconds而不是SET+EXPIRE两步操作,避免 SET 成功但 EXPIRE 失败导致 Key 永久有效。 -
大量 Key 同时过期可能导致 Redis 瞬间压力增大(缓存雪崩)。可以在过期时间上加入随机偏移,打散过期时间:
redisEXPIRE cache:key 3600 # 改为 3600 ± 300 秒的随机值,避免同时过期 -
不要对大 Key 使用过短的过期时间(如频繁创建和删除大集合 Key),因为删除大 Key 可能阻塞 Redis。
-
需要精确控制过期时间点时使用
EXPIREAT,需要毫秒精度时使用PEXPIRE/PEXPIREAT。 -
Redis 7.0+ 的条件选项(
NX、XX、GT、LT)可以在很多场景下替代"先 TTL 判断再 EXPIRE"的非原子流程,使用更安全。 -
集群模式下,
EXPIRE作用于单个 Key,不受哈希槽限制;涉及多个 Key 的操作需注意槽位分布。
十二、常见问题排查
问题 1:EXPIRE 返回 1 但 Key 没有过期
确认以下几点:
- 使用
TTL key确认剩余时间是否正确。 - 是否在
EXPIRE之后又执行了SET等会清除过期时间的命令。 - Key 是否被
PERSIST移除了过期时间。 - 是否是从节点读取(从节点不会主动过期 Key,等待主节点同步)。
redis
TTL mykey
PTTL mykey
EXPIRETIME mykey
问题 2:Key 提前过期了
可能的原因:
- 内存达到
maxmemory上限,触发了内存淘汰策略,Key 被提前淘汰。 - 有其他客户端调用了
DEL、SET(覆盖)等命令删除或覆盖了 Key。 EXPIRE的秒数计算有误(注意是秒不是毫秒)。- 系统时间被修改(Redis 使用系统时间计算过期,时间跳转会影响过期判断)。
问题 3:EXPIRE 总是返回 0
可能的原因:
- Key 不存在(先
EXISTS确认)。 - 使用了条件选项(
NX/XX/GT/LT)但条件不满足。 - 传入的秒数格式不正确(必须是整数)。
问题 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 同时过期可能引发缓存雪崩,建议在过期时间上加入随机偏移以打散过期峰值。