allkeys-lru不能模拟定长队列,因其以key为单位淘汰、不感知list元素粒度,会整key删除导致队列消失,且无法保证保留最新N条;必须用LPUSH/RPUSH+LTRIM组合或Lua脚本原子化控制长度。

不能用 maxmemory + allkeys-lru 模拟固定容量队列 —— 它不保证顺序,也不控制单个 key 的长度,会误删其他 key 里的数据。
为什么 allkeys-lru 不适用于定长队列场景
Redis 的 allkeys-lru 是在内存超限时,从 整个数据库所有 key 中挑选“最久未使用”的 key 删除,不是按 list 元素粒度淘汰。哪怕你只操作一个 queue:123,只要它内部元素访问频次低、或有其他更“热”的 key(比如 session 缓存),就可能被整 key 删掉 —— 队列直接消失,不是截断。
更关键的是:LRU 判断依据是 key 的访问时间,而 LPUSH / RPUSH 并不更新整个 list 的 LRU 时间戳(Redis 6.0+ 对 list 元素级 LRU 支持极弱,且不用于淘汰决策)。所以你无法预测哪个元素会被“淘汰”,也无法保证留下的一定是最新 N 条。
-
allkeys-lru的作用对象是 key,不是 list 中的元素 - 没有机制让 Redis 知道“这个 list 只需要保留最后 100 个元素”
- 一旦触发淘汰,可能删掉完全无关的 key,破坏业务状态
真正可控的定长队列必须靠命令组合或 Lua 原子化
固定容量的本质是:每次写入后,确保该 list 的长度 ≤ N。这只能通过显式控制 list 长度来实现,常见可靠路径有两条:
-
LPUSH+LTRIM组合:写入后立刻裁剪,如LPUSH queue:item "new"→LTRIM queue:item 0 99(保前 100) -
RPUSH+LTRIM组合:如RPUSH queue:item "new"→LTRIM queue:item -100 -1(保后 100) - 封装为 Lua 脚本(推荐):把判断 + 写入 + 裁剪打包成一次原子操作,避免竞态。例如:
eval "redis.call('rpush', KEYS[1], ARGV[1]); redis.call('ltrim', KEYS[1], -tonumber(ARGV[2]), -1); return 1" 1 queue:123 "msg42" 100
这段脚本确保无论并发多少请求,queue:123 永远只存最后 100 条,不会超长,也不依赖外部逻辑判断长度。
LPUSH 和 RPUSH 在定长语义下的行为差异
选 LPUSH 还是 RPUSH,取决于你希望“最新”怎么定义:
- 用
LPUSH+LTRIM queue 0 N-1:新消息插到头,保留头部 N 个 → 队列里是“最新进来的 N 条”,顺序为插入倒序(LRANGE queue 0 -1显示最新在前) - 用
RPUSH+LTRIM queue -N -1:新消息插到尾,保留尾部 N 个 → 队列里仍是“最新进来的 N 条”,但顺序为插入正序(LRANGE queue 0 -1显示最早在前,最新在后) - 二者性能无差别,都是 O(1);但
LTRIM的负索引(如-100)在 Redis 2.8+ 完全支持,无需担心兼容性
真正容易被忽略的是:定长队列的“长度边界”必须由业务自己硬控,Redis 不提供自动截断的配置项。哪怕你设了 maxmemory 1gb,也救不了一个疯涨的 list —— 它只会等整个实例内存爆了才开始乱删 key。别把淘汰策略当成队列管理工具。


















