Redis命令:TTL

Redis TTL 命令详细教程

TTL 用于获取 Key 的剩余生存时间(Time To Live),单位为秒。它是 Redis 通用 Key 操作命令,适用于检查缓存、会话、验证码、限流计数器和分布式锁等数据距离过期还有多久。

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

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

一、命令概览

1. 基本语法

redis 复制代码
TTL key

参数说明:

参数 说明
key 要查询剩余生存时间的 Key

返回值:

场景 返回值
Key 存在且设置了过期时间 剩余生存时间,单位为秒的整数
Key 存在但没有过期时间 -1
Key 不存在或已经过期 -2

2. 最简单的示例

先写入一个带过期时间的 Key,再查询剩余时间:

redis 复制代码
SET mykey "Hello" EX 10
TTL mykey

返回值类似:

text 复制代码
(integer) 9

返回值通常略小于 10,因为从设置 Key 到执行查询已经经过了一些时间,这是正常现象。

3. 命令特性

  • TTL 是只读命令,不会修改 Key 的值或过期时间。
  • 时间单位是秒,返回值为整数。
  • 时间不足 1 秒但 Key 仍存在时,通常返回 0
  • 时间复杂度为 O(1)。
  • TTL 作用于整个 Key,适用于 String、Hash、List、Set、Sorted Set、Stream 等数据类型。

二、返回值详解

1. Key 存在且有过期时间

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

返回一个正整数,例如:

text 复制代码
(integer) 1799

这表示该 Key 预计还会保留约 1799 秒。

2. Key 存在但没有过期时间

redis 复制代码
SET permanent-key "forever"
TTL permanent-key

返回:

text 复制代码
(integer) -1

-1 表示 Key 存在,但没有设置过期时间,也不会自动过期。它不表示 Key 不存在。

3. Key 不存在

redis 复制代码
TTL no-such-key

返回:

text 复制代码
(integer) -2

Key 可能从未创建、已经被删除,或者已经过期并被 Redis 视为不存在。

4. 已过期的 Key

redis 复制代码
SET short-key "value" EX 1

等待超过 1 秒后执行:

redis 复制代码
TTL short-key

返回 -2。Redis 在访问 Key 时会执行惰性过期检查,因此已经过期的 Key 按不存在处理,而不是返回负的剩余秒数。

5. 剩余时间不足 1 秒

redis 复制代码
SET short-key "value" PX 800
TTL short-key
PTTL short-key

返回可能类似:

text 复制代码
TTL:  (integer) 0
PTTL: (integer) 760

TTL 以秒为单位并向下取整,所以 0 可能表示 Key 仍存在但剩余时间不足 1 秒。需要毫秒精度时,应使用 PTTL

三、TTL 与 PTTL 的区别

TTLPTTL 都返回剩余生存时间,区别只有时间单位和精度:

特性 TTL PTTL
时间单位 毫秒
精度 秒级,向下取整 毫秒级
Key 无过期时间 -1 -1
Key 不存在 -2 -2
适合场景 秒级倒计时、普通缓存 短时缓存、锁续期、精确限流
可用版本 Redis 1.0+ Redis 2.6+

1. 取整行为

redis 复制代码
SET mykey "value" PX 1500
TTL mykey
PTTL mykey

如果查询时剩余约 1490 毫秒,结果可能是:

text 复制代码
TTL:  (integer) 1
PTTL: (integer) 1490

如果剩余约 800 毫秒,TTL 可能返回 0,而 PTTL 仍能返回约 800

2. 判断条件的差异

text 复制代码
TTL > 0       表示通常还剩至少 1 秒
TTL == 0      表示剩余不足 1 秒,或处于临界状态
TTL == -1     Key 存在但永久有效
TTL == -2     Key 不存在或已过期

如果业务阈值是 500 毫秒,不应使用 TTL == 0 判断,而应使用:

redis 复制代码
PTTL mykey

然后在应用中判断 0 <= pttl < 500

3. 选择建议

  • 秒级精度足够时使用 TTL,语义直观。
  • 需要亚秒级精度时使用 PTTL
  • 设置命令使用 PEXPIRESET ... PX 时,查询通常也使用 PTTL,避免单位换算错误。

四、与相关命令的区别

1. TTL 与 EXPIRE

EXPIRE 设置以秒为单位的相对过期时间,TTL 查询剩余秒数:

redis 复制代码
SET cache:product:1001 "data"
EXPIRE cache:product:1001 3600
TTL cache:product:1001

EXPIRE 是写命令,返回设置是否成功;TTL 是读命令,返回剩余秒数。

2. TTL 与 PTTL

redis 复制代码
TTL key     # 返回剩余秒数
PTTL key    # 返回剩余毫秒数

两者对不存在 Key 和永久 Key 的特殊返回值相同,都是 -2-1

3. TTL 与 EXPIRETIME

TTL 返回相对时间,EXPIRETIME 返回绝对过期时间戳(秒,Redis 7.0+):

redis 复制代码
SET mykey "value" EX 3600
TTL mykey
EXPIRETIME mykey
需求 推荐命令
查询还剩多少秒 TTL
获取绝对过期时刻 EXPIRETIME
查询还剩多少毫秒 PTTL
获取绝对过期毫秒时间戳 PEXPIRETIME

4. TTL 与 EXISTS

EXISTS 只能判断 Key 是否存在,不能说明 Key 是永久有效还是带有过期时间:

redis 复制代码
EXISTS mykey  # 1 表示存在
TTL mykey     # -1 表示永久,正数表示带 TTL

TTL 返回 -2 时,通常已经足以说明 Key 不存在;如果需要更清晰的存在性语义,可配合 EXISTS 使用。

5. TTL 与 PERSIST

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

redis 复制代码
SET mykey "value" EX 60
PERSIST mykey
TTL mykey

PERSIST 成功后,TTL mykey 返回 -1

6. TTL 与 OBJECT IDLETIME

这两个命令含义完全不同:

redis 复制代码
TTL mykey              # 距离自动过期还有多久
OBJECT IDLETIME mykey  # 距离上次访问经过了多少秒

一个 Key 可以永不过期但长时间未访问,此时 TTL-1,而 OBJECT IDLETIME 可能很大。

五、过期时间如何变化

1. 使用 SET 设置过期时间

推荐在写入时原子设置 TTL:

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

EXPXSET 会同时设置值和过期时间。

2. 普通 SET 会清除过期时间

使用不带过期选项的 SET 覆盖 Key,会移除原来的过期时间:

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

此时返回 -1,因为第二次 SET 让 Key 变成永久有效。

如果要覆盖值但保留原有 TTL,可使用 Redis 支持的 KEEPTTL

redis 复制代码
SET mykey "new" KEEPTTL
TTL mykey

3. 修改集合内部数据通常保留 TTL

以下命令修改 Key 的内部内容,但不会刷新整个 Key 的过期时间:

redis 复制代码
HSET user:1001 name "Alice"
LPUSH queue:jobs "job-1"
SADD tags redis
ZADD leaderboard 100 "player-1"

示例:

redis 复制代码
HSET user:1001 name "Alice"
EXPIRE user:1001 3600
TTL user:1001
HSET user:1001 city "Shanghai"
TTL user:1001

第二次 TTL 会继续递减,而不会因为 HSET 自动恢复到 3600。

4. RENAME、COPY 与 TTL

RENAME 会把源 Key 的过期时间转移到新名称:

redis 复制代码
SET old:key "value" EX 60
RENAME old:key new:key
TTL new:key
TTL old:key

new:key 保留剩余 TTL,old:key 不存在并返回 -2

使用 COPY 时,应根据 Redis 版本和命令选项确认是否复制 TTL;需要明确控制副本过期属性时,不要仅凭经验推断,使用目标实例的命令文档确认。

六、不同数据类型中的 TTL

TTL 面向 Key,而不是某个数据结构中的单个元素:

redis 复制代码
SET string:key "hello" EX 3600
TTL string:key

HSET hash:key name "Alice"
EXPIRE hash:key 3600
TTL hash:key

RPUSH list:key a b c
EXPIRE list:key 3600
TTL list:key

SADD set:key member-1 member-2
EXPIRE set:key 3600
TTL set:key

ZADD zset:key 100 member-1
EXPIRE zset:key 3600
TTL zset:key

对于普通 Key 级过期,整个 Key 到期后,其全部内容都会失效。Redis 7.4+ 还支持 Hash 字段级过期命令,但字段级 TTL 与 TTL key 的 Key 级 TTL 是两套不同语义。

七、只读、事务与并发

1. 只读特性

TTL 不修改值、过期时间或数据库内容,不会因为查询而刷新 TTL,也不会写入 AOF。它适合用于监控和状态检查。

2. 查询结果是时间快照

连续执行查询时,返回值会自然递减:

redis 复制代码
TTL mykey
TTL mykey

两次结果的差异取决于实际经过的时间、命令处理时间和取整方式。不要要求返回值始终精确等于最初设置的秒数。

3. 先查询再操作存在竞态

下面的流程不是原子的:

redis 复制代码
TTL lock:job:1
# 根据上一步结果决定是否续期
EXPIRE lock:job:1 30

在两条命令之间,Key 可能过期、被删除或被其他客户端修改。EXPIRE 对不存在的 Key 返回 0,不会自动创建 Key,但业务判断仍可能受到竞态影响。

需要原子地检查并续期时,可使用 EXPIRE 的条件选项(Redis 7.0+)或 Lua 脚本。对于分布式锁,还必须校验锁的持有者,不能只依据 TTL 续期。

4. 在事务中使用

redis 复制代码
MULTI
TTL mykey
EXISTS mykey
EXEC

事务中的命令会排队,在 EXEC 时按顺序执行。需要注意,MULTIEXEC 之间其他客户端仍可能修改 Key,因此把多个命令放入事务并不等同于在排队期间锁定数据。

5. Pipeline 批量查询

查询多个 Key 的 TTL 时,可以通过客户端 Pipeline 减少网络往返:

text 复制代码
TTL cache:1
TTL cache:2
TTL cache:3

Pipeline 只是批量发送命令,不会自动提供跨命令的事务原子性;如果业务需要原子性,应明确使用事务或 Lua 脚本。

八、常见客户端用法

1. redis-cli

bash 复制代码
redis-cli SET session:user:1001 "active" EX 1800
redis-cli TTL session:user:1001
redis-cli TTL no-such-key
redis-cli SET permanent "forever"
redis-cli TTL permanent

2. Python(redis-py)

python 复制代码
import redis

client = redis.Redis(
    host="localhost",
    port=6379,
    decode_responses=True,
)

client.set("session:user:1001", "active", ex=1800)
ttl = client.ttl("session:user:1001")
print(ttl)  # 约为 1800

client.set("permanent", "forever")
print(client.ttl("permanent"))  # -1
print(client.ttl("no-such-key"))  # -2

3. Node.js(node-redis)

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

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

await client.set("session:user:1001", "active", { EX: 1800 });
console.log(await client.ttl("session:user:1001")); // 约为 1800

await client.set("permanent", "forever");
console.log(await client.ttl("permanent")); // -1
console.log(await client.ttl("no-such-key")); // -2

await client.quit();

4. Java(Jedis)

java 复制代码
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;

try (Jedis jedis = new Jedis("localhost", 6379)) {
    jedis.set("session:user:1001", "active", SetParams.setParams().ex(1800));

    long ttl = jedis.ttl("session:user:1001");
    System.out.println(ttl); // 约为 1800

    jedis.set("permanent", "forever");
    System.out.println(jedis.ttl("permanent")); // -1
    System.out.println(jedis.ttl("no-such-key")); // -2
}

客户端通常会把 -1-2 原样作为整数返回。业务代码应显式区分特殊值,不要把所有非正数都当成同一种状态。

九、典型业务场景

1. 缓存刷新

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

应用可以在 TTL 低于某个阈值时提前刷新缓存,减少缓存同时失效造成的请求突增。需要精确到毫秒时使用 PTTL

2. 会话有效期

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

登录态检查时,-2 通常表示会话不存在或已过期,正数表示还剩多少秒,-1 则说明会话没有自动过期保护,需要重点排查。

3. 验证码或一次性令牌

redis 复制代码
SET otp:user:1001 "582714" EX 300
TTL otp:user:1001

消费令牌时先验证值,再删除 Key。不要只检查 TTL 就认为令牌有效,还需要校验令牌内容、用户和用途。

4. 限流窗口

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

NX 可以避免每次计数时都重置窗口。生产代码还应考虑计数与首次设置过期时间之间的原子性,必要时使用 Lua 脚本。

5. 分布式锁观察

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

TTL 可以用于观察锁剩余时间,但不能单独用于安全释放或续期。释放锁时应校验 value 是否属于当前客户端,并使用原子脚本删除。

6. 延迟任务辅助检查

redis 复制代码
SET delay:task:1001 "payload" EX 60
TTL delay:task:1001

TTL 只能告诉应用还剩多少时间,不能保证在过期瞬间收到通知。需要可靠的延迟任务,应使用 Streams、专业消息队列或正确配置并监控 Keyspace Notifications。

十、过期机制与 TTL

1. 惰性过期

客户端访问 Key 时,Redis 会检查它是否已经过期。如果已过期,Redis 会将其按不存在处理,因此 TTL 返回 -2

2. 定期过期

Redis 会在后台周期性抽样检查设置了 TTL 的 Key,并清理其中已过期的 Key。没有访问的过期 Key 可能在物理上暂时仍占用内存,但逻辑上已经不可用。

3. 过期与内存淘汰不是一回事

Key 可能因为过期而删除,也可能因为达到 maxmemory 后被淘汰。使用 TTL 时若 Key 返回 -2,只能说明当前不可见,不能仅凭此命令确定是自然过期、主动删除还是内存淘汰。

4. 主从复制

在主从架构中,过期判断和删除主要由主节点完成,主节点会把删除结果传播给副本。副本上的 TTL 可能因时钟、复制延迟和读写节点不同而出现短暂差异;关键业务的状态判断应以正确的权威节点为准。

十一、性能与使用建议

  1. 时间复杂度为 O(1),单次执行开销很低。
  2. TTL 是只读命令,适合查询单个 Key 的状态。
  3. 正确区分三类返回值:正数表示带 TTL,-1 表示永久有效,-2 表示不存在或已过期。
  4. 不要把 TTL == 0 直接等同于 Key 不存在;剩余不足 1 秒时 Key 仍可能存在。
  5. 需要毫秒精度时使用 PTTL,不要通过 TTL * 1000 猜测精确剩余时间。
  6. 大量 Key 查询时使用 Pipeline,并控制轮询频率,避免监控程序制造不必要的 QPS。
  7. 不要使用 KEYS * 配合 TTL 做生产环境全量扫描,应使用 SCAN 分批遍历。
  8. 设置值和过期时间时优先使用 SET key value EX secondsSET key value PX milliseconds,避免两步操作造成 Key 没有 TTL。
  9. 对缓存设置 TTL 时加入随机偏移,可减少大量 Key 同时到期造成的缓存雪崩。
  10. 分布式锁的安全性不能只依赖 TTL,释放、续期和所有权校验应使用原子方案。

十二、常见问题排查

问题 1:TTL 返回 -2

可能原因:

可能原因 排查方法
Key 从未创建 使用 EXISTS key 确认
Key 已过期 检查设置 TTL 的时间和业务日志
Key 被删除 检查应用中的 DELUNLINK 等操作
连接了错误的实例 检查主机、端口、认证和集群路由
选择了错误的逻辑数据库 检查是否执行过 SELECT
Key 名称不一致 使用 SCAN MATCH 检查实际名称

问题 2:TTL 返回 -1

-1 表示 Key 存在但没有过期时间。常见原因包括:

  1. 创建 Key 时没有使用 EXPXEXATPXAT
  2. 后续使用不带过期选项的 SET 覆盖了原值。
  3. 某个流程调用了 PERSIST
  4. 应用把用户输入的过期时间单位误判为秒或毫秒,导致设置逻辑没有执行。
redis 复制代码
EXISTS mykey
TTL mykey
TYPE mykey

问题 3:TTL 返回 0

0 通常表示 Key 仍存在,但剩余时间不足 1 秒。用 EXISTSGET 进一步确认:

redis 复制代码
TTL mykey
EXISTS mykey

如果需要精确判断剩余时间,改用:

redis 复制代码
PTTL mykey

问题 4:TTL 比设置值小

这通常是正常现象。从执行 SETEXPIRE 到执行 TTL 的过程中,时间已经流逝。偏差明显过大时,检查是否有其他客户端重新设置过期时间、是否发生了网络延迟,或是否查询了副本节点。

问题 5:设置了 TTL 但 Key 仍然永久存在

检查设置 TTL 后是否又执行了普通 SET

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

第二个 SET 会清除 TTL。若需要保留 TTL,使用 SET ... KEEPTTL,或在覆盖时显式重新设置过期时间。

问题 6:大批量 Key 到期导致延迟

大量 Key 使用相同 TTL 可能在同一时间集中失效,引起缓存击穿和删除压力。可以为 TTL 加入随机抖动,并通过监控、限流、互斥重建或逻辑过期等方式降低峰值。

十三、完整练习

下面的示例覆盖带 TTL、永久 Key、不存在 Key、秒毫秒精度、更新 TTL 和移除 TTL 等场景。请在测试数据库执行;FLUSHDB 会清空当前逻辑数据库中的全部 Key。

redis 复制代码
FLUSHDB

# 1. 基本用法
SET demo:temp "data" EX 10
TTL demo:temp

# 2. 永久 Key
SET demo:forever "value"
TTL demo:forever

# 3. 不存在的 Key
TTL demo:not-exist

# 4. 秒级与毫秒级精度对比
SET demo:short "value" PX 1500
TTL demo:short
PTTL demo:short

# 5. 使用 EXPIRE 修改 TTL
SET demo:renew "session"
EXPIRE demo:renew 30
TTL demo:renew
EXPIRE demo:renew 60
TTL demo:renew

# 6. SET 覆盖会清除 TTL
SET demo:renew "new-value"
TTL demo:renew

# 7. KEEPTTL 保留现有 TTL
SET demo:keep "old" EX 60
SET demo:keep "new" KEEPTTL
TTL demo:keep

# 8. PERSIST 移除 TTL
SET demo:persist "value" EX 60
PERSIST demo:persist
TTL demo:persist

# 9. 集合数据类型使用 Key 级 TTL
HSET demo:hash name "Alice" age 30
EXPIRE demo:hash 3600
TTL demo:hash
HSET demo:hash city "Shanghai"
TTL demo:hash

# 10. 已过期 Key
SET demo:gone "value" EX 1
# 等待 1 秒以上后执行:
# TTL demo:gone
# EXISTS demo:gone

# 11. 与 EXISTS 组合判断
EXISTS demo:temp
TTL demo:temp
EXISTS demo:forever
TTL demo:forever

预期结果:

  • demo:tempTTL 返回小于或等于 10 的正数。
  • demo:foreverTTL 返回 -1
  • demo:not-existTTL 返回 -2
  • demo:shortTTL 可能返回 01PTTL 返回剩余毫秒数。
  • demo:renew 第二次 EXPIRE 后,TTL 被更新为约 60 秒。
  • 普通 SET demo:renew 后,TTL 返回 -1
  • SET ... KEEPTTL 后,demo:keep 仍保留剩余 TTL。
  • PERSIST demo:persist 后,TTL 返回 -1
  • HSET 修改 Hash 字段不会自动刷新 Key 的 TTL。
  • demo:gone 过期后,TTL 返回 -2EXISTS 返回 0

十四、命令速查表

需求 命令 精度或结果
获取剩余生存时间 TTL key
获取剩余生存时间 PTTL key 毫秒
设置相对过期时间 EXPIRE key seconds
设置相对过期时间 PEXPIRE key milliseconds 毫秒
按时间戳设置过期 EXPIREAT key timestamp 秒时间戳
按时间戳设置过期 PEXPIREAT key milliseconds-timestamp 毫秒时间戳
获取过期时间戳 EXPIRETIME key Redis 7.0+,秒
获取过期时间戳 PEXPIRETIME key Redis 7.0+,毫秒
移除过期时间 PERSIST key 变为永久 Key
原子写入并设置秒级 TTL SET key value EX seconds 推荐
原子写入并设置毫秒级 TTL SET key value PX milliseconds 推荐
覆盖值时保留 TTL SET key value KEEPTTL 按版本确认支持
判断 Key 是否存在 EXISTS key 01
增量遍历 Key SCAN cursor [MATCH pattern] 避免阻塞

总结

TTL 的核心作用是查询 Key 的剩余生存时间,基本语法为:

redis 复制代码
TTL key

使用时重点注意:

  • 正数表示剩余秒数,-1 表示 Key 存在但没有过期时间,-2 表示 Key 不存在或已过期。
  • TTL 以秒为单位并向下取整,剩余不足 1 秒时返回 0 不一定代表 Key 不存在。
  • 需要毫秒级精度时使用 PTTL,不要把 TTL 的结果简单乘以 1000。
  • 普通 SET 会清除已有 TTL,SET ... KEEPTTL 可在覆盖值时保留 TTL。
  • TTL 是只读快照,先查询再操作存在并发竞态;锁续期、释放和所有权校验应使用原子方案。
  • 设置值和过期时间时优先使用 SET ... EXSET ... PX,避免两步操作留下永久 Key。
相关推荐
深蓝电商API3 小时前
Redis 在分布式爬虫中的作用
redis·分布式·爬虫
编码者卢布4 小时前
【Azure Function】NodeJS Function大批量写入到Redis遇见丢失数据情况的分析
redis·microsoft·azure
MC丶科4 小时前
软考架构师90天冲刺|DAY44·Redis高级应用
数据库·数据仓库·redis·缓存·oracle·容器·规格说明书
Boop_wu5 小时前
[redis] redis 快速入门
数据库·redis·github
三8445 小时前
Redis未授权访问与四种 getshell 路径(原理 → 实操,因果不断层)
redis·web安全·ssrf·getshell
上单带刀不带妹5 小时前
小程序图片缓存问题:时间戳和版本号解决图片不更新
缓存·小程序·vue·uniapp
她说..6 小时前
Redis项目实战整理
数据库·redis·缓存
蓝速科技8 小时前
蓝速科技丨多网点涉外窗口翻译机批量部署实战指南
服务器·数据库·人工智能·缓存·语音识别
William Dawson9 小时前
Spring Boot 接入华为 MRS Redis 集群(密码认证)全流程实战
spring boot·redis·华为