一、命名用冒号分层
user:1001:profile
user:1001:follows
order:20261103:count
cache:article:8821
lock:order:1001
好处:一看就知道业务域、对象、ID;SCAN 时能按前缀过滤;监控工具能按前缀聚合。避免 u1001p 这种无分隔的写法,也别用中文和空格(转义麻烦)。
统一在团队文档里约定前缀,比每个人自由发挥强得多。
二、控制单个 key 的大小
经验阈值:
| 类型 | 建议上限 |
|---|---|
| String | 10KB(超过考虑拆或换存储) |
| Hash / List / Set / ZSet | 元素数 5000 以内 |
| 整体 | 单 key 不超过 10MB |
超过就是「大 key」。危害有三:
- 阻塞主线程:Redis 单线程执行命令,读一个 100MB 的 String 会卡住所有请求。
- 删除代价高:
DEL大 key 是同步的,会卡顿;要用UNLINK(异步删除)。 - 网络与内存放大:一次读取占用大量带宽,主从同步也受影响。
拆分方式:按时间分桶(order:202611:count)、按字段拆 Hash、分页存 List。
三、一定要设 TTL
缓存类 key 不设过期,内存只会一直涨,最后触发淘汰策略把有用的数据挤掉。规范:
- 所有缓存 key 必须有 TTL。
- TTL 加随机抖动(如
3600 + rand(0,300)),避免同一时刻大批 key 同时失效造成「缓存雪崩」。 - 会话类 TTL 与业务一致(登录态 7 天就设 7 天)。
SET cache:article:8821 "{...}" EX 3600
四、不要用 KEYS *
KEYS 会遍历所有键并阻塞主线程,生产环境等同于一次小型故障。用 SCAN 游标迭代:
SCAN 0 MATCH "user:1001:*" COUNT 100
SCAN 是渐进式的,不保证一次返回全部,也不保证元素不重复,要循环直到游标回到 0。删除大量 key 时配合 UNLINK 或 SCAN + 批量 UNLINK。
Redis 4.0+ 还支持 UNLINK(后台线程释放内存),比 DEL 温和。
五、热 key 与缓存穿透
热 key:某个 key 的 QPS 远高于其他(如首页配置、爆款商品)。单节点可能被打满,解决方式是本地缓存 + 多副本(key 加随机后缀分散到多个节点,读时随机选一个)。
缓存穿透:查询不存在的数据,每次都打到数据库。解决:
- 缓存空值(设短 TTL,如 60 秒)。
- 布隆过滤器提前拦截。
- 接口层做参数校验与限流。
缓存击穿:热点 key 过期的瞬间大量请求同时回源。解决:互斥锁重建,或逻辑过期(值里带过期时间,异步刷新)。
缓存雪崩:大批 key 同时失效。解决:TTL 加随机、多级缓存、Redis 集群高可用。
六、内存与淘汰策略
CONFIG GET maxmemory
CONFIG GET maxmemory-policy
常见策略:
| 策略 | 行为 |
|---|---|
noeviction | 不淘汰,写满报错(默认,危险) |
allkeys-lru | 所有 key 里淘汰最久未用的(纯缓存推荐) |
volatile-lru | 只在设了 TTL 的 key 里淘汰 |
allkeys-lfu | 按访问频率淘汰(更抗突发) |
纯缓存场景用 allkeys-lru;如果 Redis 同时存了不能丢的数据(如队列),用 volatile-* 并确保持久化配置正确。
七、排查命令
redis-cli --bigkeys # 找大 key(抽样)
redis-cli --hotkeys # 找热 key(需 LFU 策略)
redis-cli --scan --pattern "user:*" | head
redis-cli info memory # 内存分布
redis-cli slowlog get 10 # 慢命令
--bigkeys 是抽样扫描,不是精确统计,但足够发现明显问题。slowlog 能抓到耗时超过阈值的命令(slowlog-log-slower-than 默认 10000 微秒)。
小结
键名用冒号分层;单 key 控制在 10MB / 5000 元素内;缓存必须设 TTL 且加随机抖动;禁用 KEYS *,用 SCAN;大 key 用 UNLINK 删;热点与穿透分别用多副本和空值缓存应对。