redis-cli --bigkeys 不能识别导致频繁淘汰的大key,因其仅统计逻辑大小(如strlen),而非真实内存占用,且忽略访问频次、写入速率、数据库编号等关键因素;真正应结合 info memory/info stats 观察淘汰指标,并用 memory usage + scan 精准定位高内存+高访问的key。

redis-cli --bigkeys 不能直接识别“导致频繁淘汰”的大Key,它只统计逻辑大小,和内存实际占用、淘汰行为无直接关联。
为什么 --bigkeys 扫出来的“最大 Key”不一定触发淘汰
Redis 的淘汰(eviction)决策依据是 used_memory_dataset 和配置的 maxmemory,而 redis-cli --bigkeys 输出的 “5242880 bytes” 实际是 STRLEN 结果,不是真实内存占用。一个看似只有 1KB 的 JSON 字符串,反序列化后可能因 Redis 内部编码(如从 embstr 升级为 raw)、SDS 额外头开销、或包含大量指针结构(如 ziplist → hashtable),实际吃掉 5MB 内存——但 --bigkeys 完全看不到。
更关键的是:频繁淘汰往往由“一堆中等大小 Key + 高频写入”共同导致,而非单个显眼大 Key。比如每秒新增 1000 个 20KB 的 session string,--bigkeys 可能只扫到最老的那个,却漏掉正在快速堆积的新生代。
-
--bigkeys默认只查db0,业务 key 若在db3或db15,必须加-n 3或-n 15 - 它不区分 key 的访问频次,无法反映“谁正在被高频写入并推高内存”
- 扫描期间若发生
DEL/SET,结果会滞后,且无法捕获瞬时峰值
真正该看的指标:info memory + eviction 统计
先确认淘汰是否真在发生、以及发生在哪类 key 上:
redis-cli info memory | grep -E "(used_memory_dataset|maxmemory|mem_fragmentation_ratio)" redis-cli info stats | grep -E "(evicted_keys|expired_keys|keyspace_hits|keyspace_misses)"
重点关注:evicted_keys 是否持续增长;used_memory_dataset 是否长期 > 85% maxmemory;mem_fragmentation_ratio > 1.5 表示内存碎片严重,也容易误触发淘汰。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
此时再结合 --bigkeys,只是辅助手段——例如发现 evicted_keys 每分钟涨 500,而 --bigkeys 显示 user:cache:* 类型 hash 占据 top3,就该立刻去查这些 hash 的 MEMORY USAGE 和写入频率,而不是盯着它“有多少 field”。
用 MEMORY USAGE + SCAN 替代 --bigkeys 做精准定位
要锁定真正吃内存、且正在参与淘汰的 key,必须绕过 --bigkeys,改用 MEMORY USAGE 配合 SCAN:
-
MEMORY USAGE返回近似真实内存字节数(Redis 4.0+),比STRLEN/HLEN可靠得多 - 不要用
KEYS *,它会阻塞主线程,线上禁用 - 用
SCAN cursor COUNT 500分批,每次最多查 500 个 key,降低对主节点压力 - 对每个 key 调用
MEMORY USAGE key,记录超过阈值(如 1MB)的 key 和其TYPE - 额外加一步:用
OBJECT FREQ key查访问频次(需开启maxmemory-policy allkeys-lfu),高频 + 大内存 = 淘汰高危户
示例 Python 片段(简化版):
import redis
r = redis.Redis(host='127.0.0.1', db=0)
cursor = 0
while True:
cursor, keys = r.scan(cursor, count=500)
for k in keys:
try:
mem = r.memory_usage(k)
if mem > 1024*1024: # >1MB
print(f"{k} {r.type(k)} {mem} bytes")
except:
pass
if cursor == 0:
break
清理时别用 DEL,优先用 UNLINK + 分片删除
即使定位到真·大 Key,直接 DEL 仍可能阻塞主线程数秒,加剧淘汰抖动。正确做法分两层:
- 立即释放内存:用
UNLINK key,它把删除操作丢进后台线程异步执行,主线程几乎无感 - 对无法 UNLINK 的场景(如集群模式下跨 slot):拆成小批量,例如 hash 类型用
HSCAN分页获取 field,再HDEL一批 100 个 - String 类型超大值:确认是否可截断或降级,比如把原始日志 blob 改存为 URL,value 只留路径
特别注意:如果该 key 正被客户端高频读取,UNLINK 后首次访问会返回 nil,业务需有兜底逻辑,不能假设“删了就彻底消失”。










