第 4 章:键管理与过期策略
jerry北京市2026年4月30日Go 4 次阅读 约 13 分钟

掌握键的生命周期管理、过期删除策略、内存淘汰机制和键空间通知。
4.1 键的基本操作
# 查看键
KEYS * # 查看所有键(生产禁用!会阻塞)
KEYS user:* # 模式匹配
SCAN 0 MATCH user:* COUNT 100 # 渐进式扫描(生产推荐)
DBSIZE # 当前数据库的键数量
# 键信息
EXISTS key # 是否存在(1/0)
TYPE key # 数据类型
OBJECT ENCODING key # 底层编码
OBJECT REFCOUNT key # 引用计数
OBJECT IDLETIME key # 空闲时间(秒)
OBJECT FREQ key # 访问频率(LFU 模式下)
DEBUG OBJECT key # 详细信息(调试用)
# 键操作
RENAME key newkey # 重命名(newkey 存在则覆盖)
RENAMENX key newkey # 重命名(newkey 存在则失败)
COPY source dest # 复制键(Redis 6.2+)
DUMP key # 序列化
RESTORE key 0 serialized # 反序列化恢复
# 排序
SORT mylist # 对列表/集合排序
SORT mylist DESC LIMIT 0 10 # 降序,取前 10
SCAN 系列命令
生产环境中,KEYS 命令会阻塞 Redis,必须用 SCAN 替代:
# SCAN:遍历所有键
SCAN 0 MATCH user:* COUNT 100
# 返回:下一个游标 + 匹配的键列表
# 游标为 0 时表示遍历完成
# HSCAN:遍历 Hash 的字段
HSCAN myhash 0 MATCH field* COUNT 100
# SSCAN:遍历 Set 的成员
SSCAN myset 0 MATCH pattern* COUNT 100
# ZSCAN:遍历 Sorted Set 的成员
ZSCAN myzset 0 MATCH pattern* COUNT 100
SCAN 的特点:
- 渐进式遍历,不会阻塞
- 可能返回重复元素,客户端需要去重
- COUNT 是建议值,不是精确值
- 遍历过程中新增/删除的键可能被遗漏或重复
4.2 过期时间
# 设置过期时间
EXPIRE key 60 # 60 秒后过期
PEXPIRE key 60000 # 60000 毫秒后过期
EXPIREAT key 1704067200 # 在指定 Unix 时间戳过期
PEXPIREAT key 1704067200000 # 毫秒级时间戳
# 查看剩余时间
TTL key # 剩余秒数(-1 永不过期,-2 不存在)
PTTL key # 剩余毫秒数
# 移除过期时间
PERSIST key # 变为永不过期
# 设置值的同时设置过期
SET key value EX 60 # 秒
SET key value PX 60000 # 毫秒
SETEX key 60 value # 等价于 SET + EXPIRE
注意事项:
- 对已有过期时间的 key 执行
SET会清除过期时间 RENAME不会影响过期时间EXPIRE的时间精度是秒级,PEXPIRE是毫秒级
4.3 过期删除策略
Redis 使用两种策略配合删除过期键:
惰性删除(Lazy Expiration)
- 访问 key 时检查是否过期,过期则删除
- 优点:对 CPU 友好,只在访问时检查
- 缺点:大量过期 key 不被访问时会占用内存
定期删除(Active Expiration)
- Redis 每秒执行 10 次(由
hz配置控制):- 随机抽取 20 个设置了过期时间的 key
- 删除其中已过期的 key
- 如果过期比例 > 25%,重复步骤 1
- 每次执行不超过 25ms,避免阻塞
┌─────────────────────────────────────┐
│ 过期删除策略 │
├─────────────────────────────────────┤
│ │
│ 惰性删除:访问时检查 │
│ ┌─────┐ ┌──────┐ ┌──────┐ │
│ │ GET │ → │过期? │ → │ 删除 │ │
│ └─────┘ └──────┘ └──────┘ │
│ │
│ 定期删除:后台定时扫描 │
│ ┌──────────┐ ┌──────┐ │
│ │随机抽样20│ → │删除过期│ │
│ │个key │ │的key │ │
│ └──────────┘ └──────┘ │
│ 过期比例>25% → 继续抽样 │
│ │
└─────────────────────────────────────┘
4.4 内存淘汰策略
当内存达到 maxmemory 限制时,Redis 根据淘汰策略决定删除哪些 key:
| 策略 | 说明 |
|---|---|
noeviction |
不淘汰,写入报错(默认) |
allkeys-lru |
所有 key 中淘汰最近最少使用的(推荐) |
allkeys-lfu |
所有 key 中淘汰最不经常使用的(Redis 4.0+) |
allkeys-random |
所有 key 中随机淘汰 |
volatile-lru |
有过期时间的 key 中淘汰 LRU |
volatile-lfu |
有过期时间的 key 中淘汰 LFU |
volatile-random |
有过期时间的 key 中随机淘汰 |
volatile-ttl |
有过期时间的 key 中淘汰 TTL 最小的 |
# 查看当前策略
CONFIG GET maxmemory-policy
# 设置策略
CONFIG SET maxmemory-policy allkeys-lru
LRU vs LFU
-
LRU(Least Recently Used):淘汰最久没被访问的
- 适合:访问模式有明显的时间局部性
- Redis 使用近似 LRU(采样淘汰),不是精确 LRU
-
LFU(Least Frequently Used):淘汰访问频率最低的
- 适合:有热点数据的场景
- Redis 4.0+ 支持,使用 Morris 计数器
选择建议:
- 通用场景:
allkeys-lru - 有明显热点数据:
allkeys-lfu - 缓存 + 持久数据混合:
volatile-lru
4.5 键空间通知
Redis 可以在键发生变化时发送通知:
# 开启键空间通知
CONFIG SET notify-keyspace-events KEA
# K:键空间通知(按 key 名)
# E:键事件通知(按事件类型)
# A:所有事件
# g:通用命令(DEL, EXPIRE, RENAME...)
# $:String 命令
# l:List 命令
# s:Set 命令
# h:Hash 命令
# z:Sorted Set 命令
# x:过期事件
# e:淘汰事件
订阅通知:
# 终端 1:订阅所有键的过期事件
SUBSCRIBE __keyevent@0__:expired
# 终端 2:设置一个会过期的键
SET mykey "hello" EX 5
# 5 秒后终端 1 收到通知:
# "mykey"
应用场景:
- 订单超时自动取消
- 缓存过期后触发数据刷新
- 会话过期通知
4.6 大 Key 扫描
大 Key 会导致阻塞、内存不均、网络拥塞等问题:
# 使用 redis-cli 扫描大 key
redis-cli --bigkeys
# 使用 MEMORY USAGE 查看单个 key 的内存
MEMORY USAGE mykey
# 使用 SCAN + 应用层判断
# 伪代码:
# for key in SCAN:
# size = MEMORY USAGE key
# if size > threshold:
# report(key, size)
大 Key 的定义(经验值):
- String > 10KB
- Hash/Set/Sorted Set/List > 5000 个元素
大 Key 的删除:
# 小 key 直接删除
DEL key
# 大 key 异步删除(Redis 4.0+,不阻塞)
UNLINK key
4.7 面试要点
-
Redis 的过期删除策略?
- 惰性删除 + 定期删除配合使用
-
内存淘汰策略有哪些?推荐哪个?
- 8 种策略,通用场景推荐
allkeys-lru
- 8 种策略,通用场景推荐
-
LRU 和 LFU 的区别?
- LRU 淘汰最久没访问的,LFU 淘汰访问频率最低的
-
为什么不能用 KEYS 命令?
- KEYS 会遍历所有键,O(N) 复杂度,阻塞 Redis
- 用 SCAN 渐进式遍历替代
-
大 Key 有什么危害?如何处理?
- 阻塞、内存不均、网络拥塞
- 用 UNLINK 异步删除,拆分为小 key
练习
- 使用 SCAN 遍历所有以
user:开头的键 - 设置不同的过期时间,观察 TTL 变化
- 配置
maxmemory和淘汰策略,测试内存满时的行为 - 开启键空间通知,监听键的过期事件
评论
登录 后发表评论
暂无评论