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 陷阱

  1. 派生 Key 忘记设 TTL:如果你给 user:1001:profile 设了 600 秒 TTL,但 user:1001:friends 没设 TTL,好友列表就会永远存在。
  2. 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 过期时触发副作用。

常见坑点

  1. 在生产环境使用 KEYS 命令KEYS * 会阻塞服务器。请用 SCAN 替代。
  2. Key 过长:1KB 的 Key 在百万级条目下会浪费大量内存。
  3. 没有命名空间隔离:在共享实例中不加前缀,Key 冲突不可避免。
  4. 硬编码 Key 字符串:使用辅助函数来构建 Key——减少拼写错误并强制执行规范。
  5. 忽略 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。在这里练习,别在线上服务器试。