volatile-lru 不适合作为实时排行榜默认淘汰策略,因其仅按是否设ttl筛选、不考虑访问热度,易误删高频读写的排行榜key;应分离周期榜(设ttl+volatile-lru)与实时榜(不设ttl+allkeys-lru调高maxmemory-samples),禁用allkeys-lfu以防运营拉取污染lfu计数。

volatile-lru 是实时排行榜的默认陷阱
直接用 volatile-lru 管理 ZSET 排行榜,等于把“高频读写但生命周期明确”的数据,扔进一个只认“是否带 TTL”、不认“访问热度”的淘汰池里。它不会因为你刚 ZADD 了 10 万条就手下留情,只要内存压到 maxmemory 上限,且该 ZSET key 在 volatile 池里排在 LRU 队尾,就会被整块清掉——哪怕它正被前端每秒轮询。
必须让排行榜 key 参与 allkeys-lru,但得控制淘汰权重
真实场景中,小时榜(rank:hour:2026090308)、日榜(rank:day:20260903)这类数据天然有明确过期时间,也天然需要被快速淘汰;而实时榜(rank:live)往往要长期存活、高频更新。这两类不该混在同一个淘汰逻辑里。
- 给小时/日榜等周期性榜单统一加
EXPIRE,走volatile-lru—— 它们本就该随时间自然退场 - 给实时榜(
rank:live)不设 TTL,让它成为“永久 key”,强制排除在volatile-*策略之外 - 改用
allkeys-lru,但通过maxmemory-samples调高采样数(比如从默认 5 改为 20),降低误杀概率:Redis 随机采样时更可能命中那些真正冷门的 key,而不是刚被ZRANGE访问过的rank:live - 配合
redisObject.lru字段的更新机制,确保每次ZADD/ZINCRBY都刷新 LRU 时间戳 —— 这是 Redis 内部自动做的,不用额外干预
别依赖 LFU,尤其对 ZSET 排行榜
allkeys-lfu 看起来更“聪明”,但它对排行榜业务反而危险:一次后台运营拉取全量 ZRANGE rank:live 0 -1 WITHSCORES,就会把整个 ZSET 的 LFU 计数全部抬高。之后哪怕这个榜再也没人访问,它也会在淘汰队列里顽固地卡在高位,挤占本该留给其他缓存的空间。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
-
LFU适合用户画像、商品详情页这类“长期稳定热点”,不适合“写多读少+偶发全量拉取”的排行榜 -
ZSET本身没有单 key 级别的 LFU 计数,整个 key 的计数由其redisObject维护,一次全量读就污染全部成员 - 如果真要用 LFU,务必搭配
lfu-log-factor和lfu-decay-time调优,否则新写入的实时榜永远追不上旧榜的计数
真正防误删的关键:分离存储 + 显式 TTL 控制
最稳妥的做法不是调策略参数,而是从数据建模上隔离风险。Redis 不会帮你区分“这个 ZSET 是临时榜还是实时榜”,你得用 key 命名和 TTL 设置主动划界。
- 命名规范:
rank:hour:2026090308(带时间戳)、rank:live:main(无时间戳)、rank:live:backup(备用实时榜) - TTL 设置:周期榜用
SETEX rank:hour:2026090308 3600 ...;实时榜一律不设 TTL,或设极长 TTL(如 30 天)并靠运维脚本定期清理 - 验证手段:
SCAN 0 MATCH rank:hour:* COUNT 1000 | xargs -n1 ttl查周期榜是否都带 TTL;SCAN 0 MATCH rank:live:* COUNT 1000 | xargs -n1 ttl查实时榜是否返回 -1
复杂点在于,allkeys-lru 下的实时榜仍可能被误删——只要它真的长时间没被访问,且内存压力极大。所以,生产环境必须配监控:盯紧 INFO memory 中的 evicted_keys 和 keyspace_hits,一旦发现实时榜 key 被驱逐,说明要么访问断崖下跌,要么内存配置已逼近临界。










