结论:allkeys-lru 不淘汰未设过期时间的 key,因其仅对 volatile(带 EXPIRE)key 生效;静态资源缓存应选 volatile-lru 策略,并强制写入时设置过期时间(如 SETEX),才能实现可靠自动淘汰。

直接说结论:用 allkeys-lru 策略缓存静态资源可行,但必须配合显式设置过期时间(EXPIRE 或 SETEX),否则冷数据不会被自动淘汰——因为 allkeys-lru 只对「设置了过期时间的 key」生效,而 allkeys-lru 本身不等于「所有 key 都参与 LRU 淘汰」。
为什么 allkeys-lru 实际上不淘汰没设过期时间的 key
Redis 4.0+ 的淘汰策略命名有迷惑性:allkeys-lru 表示“在所有 key 中按 LRU 淘汰”,但它有个隐含前提:只有设置了 EXPIRE 的 key 才会被纳入淘汰候选池。没设过期时间的 key(即永不过期)会被 Redis 视为“必须保留”,哪怕内存快爆了也不会动它们。
- 现象:你往 Redis 里塞了一堆
SET static:img:123 "data"(无过期),再配maxmemory-policy allkeys-lru,内存打满后,这些 key 依然纹丝不动,反而把后来带SETEX的热 key 淘汰了 - 原因:Redis 内部用两个独立结构管理 key——一个存实际数据,一个存过期时间(
expires dict)。淘汰逻辑只扫描后者非空的 key - 验证方式:执行
INFO memory,看expired_keys和evicted_keys是否持续增长;若expired_keys == 0,说明根本没 key 参与淘汰
静态资源缓存该怎么做才真正“自动交替”
静态资源(如 CSS、JS、图片 Base64、模板片段)通常不更新,但也不是永久有效——上线新版本后旧资源就该下线。靠纯 LRU 不可靠,得靠「过期时间 + 淘汰策略」双保险。
- 写入时强制加过期:用
SETEX static:js:main.js 3600 "{content}"(1小时),而不是SET;或统一在应用层封装写入函数,自动补EXPIRE - 过期时间别拍脑袋:静态资源可按发布周期设,比如 CI/CD 每次构建后资源 hash 变更,那过期时间设成 24 小时足够覆盖灰度窗口
- 避免雪崩:批量写入时不要全设同一秒过期,用
RAND() % 300在基础值上加随机偏移(如3600 + random(0,300)) - 监控兜底:用
redis-cli --bigkeys定期扫出超大 key;用MEMORY USAGE查单个静态资源实际占多少内存,防止 base64 图片撑爆实例
allkeys-lru 和 volatile-lru 到底选哪个
对纯静态资源缓存场景,volatile-lru 更合适——它只淘汰带过期时间的 key,语义清晰,且不会误伤你未来可能混存的永不过期配置类 key(比如 config:feature_flags)。
-
allkeys-lru适合「所有数据都可丢」的场景,比如 session 缓存,但静态资源一般要求强一致性(不能返回 404 的旧路径),所以你其实并不想让它淘汰任意 key - 如果硬要用
allkeys-lru,必须确保整个 Redis 实例只存静态资源,且所有写入必带过期;否则混入永不过期 key 后,淘汰效率会断崖下跌 - 配置命令:
CONFIG SET maxmemory-policy volatile-lru;注意该命令不持久化,需同步写进redis.conf的maxmemory-policy行
真正容易被忽略的点是:Redis 的淘汰不是实时触发的。即使内存已超 maxmemory,它也只在每次写入时抽样检查(默认每次检查 5 个 key),所以冷数据可能滞留数分钟才被清理。如果你依赖“写完立刻腾空间”,得调大 maxmemory-samples(比如设成 10),但会增加 CPU 开销。











