Redis Key 设计规范:前缀、版本控制与 TTL 管理
设计不良的 Redis Key 是一颗定时炸弹。它们会导致命名空间冲突、让调试变得痛苦,并在迁移过程中悄无声息地破坏你的应用。本指南涵盖了经过实战检验的 Key 设计模式。
问题场景
想象你有一个不断增长的应用。多个服务共享同一个 Redis 实例。一个团队用 user:123 存会话数据,另一个团队也用 user:123 存用户画像缓存。结果——数据被静默覆盖,日志里零报错。
Key 命名规范
好的 Redis Key 应该是自文档化的。业界广泛采用冒号作为分隔符:
<服务>:<实体>:<ID>:<字段>
编辑器实战
SET app:user:1001:profile '{"name":"Alice","role":"admin"}'
SET app:user:1001:session "abc123token"
SET app:order:5001:status "shipped"
GET app:user:1001:profile
经验法则
- 使用冒号
:作为分隔符(Redis 惯例,Redis Cluster 也用它来计算 hash slot)。 - Key 长度控制在 512 字节以内。短 Key 省内存——
u:1001:p虽然短但不可读,需要找到平衡。 - 避免空格和特殊字符。
- 统一使用小写。
多租户/多服务的前缀策略
当多个服务共享 Redis 实例时,用服务名作前缀:
SET auth:session:user:1001 "token_xyz" EX 3600
SET cache:product:2001 '{"name":"Widget","price":9.99}' EX 600
SET ratelimit:api:user:1001 "15" EX 60
这样做的好处:
- 用
SCAN+ 模式匹配监控每个服务的内存使用。 - 可以只清除某个服务的数据,而不影响其他服务。
- 可以按逻辑命名空间设置不同的淘汰策略。
Key 版本控制
当数据结构发生变化时,旧的缓存数据可能导致 Bug。Key 版本控制可以解决这个问题:
SET cache:v2:user:1001 '{"name":"Alice","role":"admin","dept":"eng"}'
GET cache:v2:user:1001
迁移期间,可以先读 v2,读不到再回退到 v1:
# 应用伪代码:
# data = GET cache:v2:user:1001
# if data is nil:
# data = migrate(GET cache:v1:user:1001)
# SET cache:v2:user:1001 data
迁移完成后,清理旧 Key:
SCAN 0 MATCH cache:v1:* COUNT 100
TTL 管理
每个缓存 Key 都应该有 TTL。没有 TTL 的 Key 就是等待爆发的内存泄漏。
设置 TTL
SET session:user:1001 "token_abc" EX 3600
TTL session:user:1001
SET temp:otp:user:1001 "482910" PX 120000
PTTL temp:otp:user:1001
访问时刷新 TTL
对于会话类数据,每次访问时刷新 TTL 实现滑动过期:
GET session:user:1001
EXPIRE session:user:1001 3600
或使用 GETEX(Redis 6.2+)原子性地获取并刷新:
SET session:user:1001 "token_abc" EX 3600
GETEX session:user:1001 EX 3600
常见 TTL 陷阱
- 派生 Key 忘记设 TTL:如果你给
user:1001:profile设了 600 秒 TTL,但user:1001:friends没设 TTL,好友列表就会永远存在。 - TTL 漂移:如果你 SET 一个 Key 时不带 EX,它会移除已有的 TTL。更新缓存值时务必带上 EX/PX。
SET mykey "value1" EX 300
TTL mykey
SET mykey "value2"
TTL mykey
运行上面的命令——你会看到第二次 SET 后 TTL 消失了。这是一个经典的生产环境 Bug。
Key 过期机制
惰性过期 + 主动过期
Redis 使用两种机制:
- 惰性过期:访问 Key 时检查是否过期,如果过期则删除并返回 nil。
- 主动过期:Redis 定期随机采样有 TTL 的 Key,删除已过期的。
这意味着过期的 Key 可能会在内存中短暂停留。对于内存敏感的场景,需要注意这一点。
Keyspace 通知
你可以订阅过期事件来触发清理逻辑:
CONFIG SET notify-keyspace-events Ex
然后在应用中订阅 __keyevent@0__:expired 频道,在 Key 过期时触发副作用。
常见坑点
- 在生产环境使用 KEYS 命令:
KEYS *会阻塞服务器。请用SCAN替代。 - Key 过长:1KB 的 Key 在百万级条目下会浪费大量内存。
- 没有命名空间隔离:在共享实例中不加前缀,Key 冲突不可避免。
- 硬编码 Key 字符串:使用辅助函数来构建 Key——减少拼写错误并强制执行规范。
- 忽略 Key 基数:如果你的 Key 模式会生成数百万个唯一 Key,提前规划内存预算。
在线编辑器试一试
前往 Redis 在线编辑器 实验这些模式:
SET app:v1:user:1001:profile '{"name":"Alice"}' EX 600
TTL app:v1:user:1001:profile
SET app:v1:user:1001:profile '{"name":"Alice","v":2}'
TTL app:v1:user:1001:profile
注意第二次 SET 后 TTL 是怎么消失的?这就是那种在生产环境咬你一口的微妙 Bug。在这里练习,别在线上服务器试。