Redis 内存到上限之后的行为,由 maxmemory-policy 决定。选错了,轻则缓存命中率下降,重则数据丢失、请求变慢。
先看现状
redis-cli INFO memory | grep -E 'used_memory_human|maxmemory_human|maxmemory_policy'
redis-cli INFO stats | grep evicted_keys
evicted_keys 持续增长说明在频繁淘汰——要么内存不够,要么策略不合适。
八种策略
| 策略 | 淘汰范围 | 说明 |
|---|---|---|
| noeviction | 不淘汰 | 写满报错,默认值 |
| allkeys-lru | 所有 key | 淘汰最久未使用的 |
| allkeys-lfu | 所有 key | 淘汰访问频率最低的 |
| allkeys-random | 所有 key | 随机淘汰 |
| volatile-lru | 设了过期时间的 | 只淘汰有 TTL 的 |
| volatile-lfu | 设了过期时间的 | 同上,按频率 |
| volatile-random | 设了过期时间的 | 同上,随机 |
| volatile-ttl | 设了过期时间的 | 优先淘汰剩余时间短的 |
怎么选
Redis 只做缓存(最常见):allkeys-lru。所有 key 都是可丢的,按最近使用情况淘汰,命中率最高。
Redis 既做缓存又存不能丢的数据(比如队列、分布式锁):noeviction。写满直接报错,逼你扩容或清理,不会悄悄删掉重要数据。这时候缓存部分应该单独用一个实例。
有明确 TTL 的临时数据:volatile-lru。只淘汰设了过期时间的,没设 TTL 的永久数据不动。风险是如果所有 key 都没设 TTL,退化成 noeviction 报错。
热点差异明显:allkeys-lfu。LFU 按访问频率淘汰,比 LRU 更能保住「一直有人访问但不够新」的热点。缺点是 LFU 计数需要时间积累,新 key 容易吃亏(Redis 4.0 之后有计数衰减和初始计数缓解)。
LRU 不是真的 LRU
Redis 的 LRU 是近似实现:采样若干个 key,淘汰其中最久未使用的,不是全局精确排序。这样省内存也快,够用。
maxmemory-samples 默认 5,调大更接近真实 LRU,但更耗 CPU。10 是个不错的折中。
内存该设多大
留出余量。Redis 在 fork 做 RDB 快照时,写时复制会额外占用内存,极端情况接近翻倍。所以:
maxmemory = 物理内存 × 0.6 ~ 0.7
还要注意 used_memory 之外的碎片率:
redis-cli INFO memory | grep mem_fragmentation_ratio
大于 1.5 说明碎片多,可以开 activedefrag yes,或者挑低峰重启。
我的配置
maxmemory 4gb
maxmemory-policy allkeys-lru
maxmemory-samples 10
timeout 300
tcp-keepalive 60
缓存实例用 allkeys-lru;队列和锁用另一个实例,noeviction + AOF。分开之后,两种用途互不干扰,也更容易定位问题。
一个教训
曾经把验证码、session 和缓存放在同一个实例,策略设成 allkeys-lru。某天缓存写入量暴增,把 session 全淘汰了,所有用户被踢下线。
结论:不同用途的 key 分实例,比调策略有效得多。
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:https://mi-blog.cn/archives/692
共有 0 条评论