Redis实例内存使用率持续上升,排查日志又看不到明显的写入高峰,此时最直接的怀疑对象就是大key。大key不仅占用大量内存,还可能造成主线程阻塞、过期删除卡顿以及网络传输缓慢。Redis官方客户端redis-cli自带了一个--bigkeys选项,可以在不阻塞服务的前提下扫描整个键空间,快速找出每种数据类型中体积最大的键。本文会从用法、原理和注意事项三个角度展开,帮助你安全高效地完成大key排查。

一、--bigkeys 基础用法
先明确一点:--bigkeys并不是Redis服务端的原生命令,而是redis-cli工具提供的一个扫描选项。它封装了一整套遍历逻辑,用户只需要在redis-cli后面加上该参数即可使用。最基本的执行方式如下:
redis-cli --bigkeys
如果Redis实例不在本机,或者端口、密码有变化,可以在命令前指定连接参数。连接远程Redis时,常见写法如下:
redis-cli -h 127.0.0.1 -p 6379 -a your_password --bigkeys
如果使用Redis 6.0以上的ACL权限体系,则建议使用--user和--pass参数,或者通过环境变量REDISCLI_AUTH传递密码,避免密码直接出现在进程列表中。对于数据量较大的实例,可以直接加上-i参数控制扫描间隔。例如-i 0.1表示每执行100次SCAN命令后休眠0.1秒,能明显降低扫描对在线业务的CPU占用。
redis-cli -h 127.0.0.1 -p 6379 -a your_password --bigkeys -i 0.1
执行后redis-cli会打印扫描进度,并持续更新每种类型当前发现的最大key。扫描结束后会输出一个汇总报告,其中包含每种数据类型的最大键以及对应的大小指标。
二、扫描原理与性能影响
--bigkeys的核心遍历方式是SCAN命令。与KEYS *不同,SCAN通过游标分批返回键名,每次只处理一小部分数据,避免一次性返回全部键导致Redis主线程长时间阻塞。对于扫描到的每个键,客户端会先发送TYPE命令判断其类型,然后根据类型执行对应的长度统计命令:字符串使用STRLEN,列表使用LLEN,集合使用SCARD,有序集合使用ZCARD,哈希使用HLEN。每次只记录超过当前最大值的键,因此不需要缓存整个键空间。
从Redis服务端看,SCAN命令本身是分批次执行的,单次执行时间很短,通常不会阻塞事件循环。但整个扫描过程会产生大量命令往返,如果数据集有几百万甚至上千万个key,CPU占用、网络包数和QPS都会明显增加。默认情况下redis-cli不会主动暂停,可能让从节点或低配实例的CPU冲到较高水位。因此生产环境建议配置-i参数,并在业务低谷期执行。
还有一点容易忽略:--bigkeys只根据数据类型本身的大小指标来判断,比如哈希表的字段数、字符串的字节长度,并不能完全反映实际内存占用。一个包含100个字段但每个字段值很小的hash,可能与一个只有10个字段但字段值很大的hash占用的内存一样甚至更少。要获得更真实的内存数据,需要结合后续的内存分析命令进一步确认。
三、解读输出结果并确认内存占用
运行redis-cli --bigkeys后,输出中会显示类似下文的内容:
# Scanning the entire keyspace to find biggest keys as well as # average sizes per key type. You can use -i 0.1 to sleep 0.1 sec # per 100 SCANS commands (not usually needed). [00.00%] Biggest string found so far '"user:10001"' with 2048 bytes [12.34%] Biggest hash found so far '"order:20240301"' with 3589 fields [58.90%] Biggest list found so far '"task:queue:backup"' with 120000 items -------- summary ------- Sampled 1000000 keys in the keyspace! Total key length in bytes is 18900000 (avg len 18.90) Biggest string found '"user:10001"' has 2048 bytes Biggest hash found '"order:20240301"' has 3589 fields Biggest list found '"task:queue:backup"' has 120000 items Biggest set found '"device:online"' has 8000 members Biggest zset found '"score:rank"' has 12000 members 0 strings with 0 bytes (00.00% of keys, avg size 0.00) 0 lists with 0 items (00.00% of keys, avg size 0.00) 0 sets with 0 members (00.00% of keys, avg size 0.00) 0 hashs with 0 fields (00.00% of keys, avg size 0.00) 0 zsets with 0 members (00.00% of keys, avg size 0.00)
摘要部分会列出每种类型的最大键及其大小。这里的大小对字符串是字节数,对其他类型是元素数量或字段数量。看到可疑键后,可以进一步使用MEMORY USAGE命令确认真实内存占用:
redis-cli MEMORY USAGE user:10001
对于字符串还可以用STRLEN查看长度;对于哈希可以用HLEN查看字段数,再用HSCAN抽样查看字段值大小。需要注意的是,MEMORY USAGE在Redis 4.0及以上版本才有,并且它会返回键值对以及内部结构的实际内存分配估算值。它比仅看元素数更接近真实情况。
如果你需要输出所有超过阈值的大key,而不只是每种类型最大一个,可以使用redis-cli --scan配合脚本逐个检查。
redis-cli --scan --pattern 'user:*' | while read key; do
type=$(redis-cli type "$key")
if [ "$type" = "string" ]; then
len=$(redis-cli strlen "$key")
if [ "$len" -gt 10240 ]; then
echo "$key $len"
fi
fi
done
四、生产环境安全操作与替代方案
即使--bigkeys基于SCAN,在大型生产实例上仍会消耗可观资源。更稳妥的做法是在从节点上执行,主节点继续正常处理读写请求。可以通过INFO replication确认从节点状态,或者在连接串中直接指向从节点地址。对于使用Redis Cluster的情况,--bigkeys只会扫描当前连接节点上的keyspace,不会自动汇总整个集群的数据。如果集群包含多个主节点,需要对每个主节点分别执行,或者编写脚本遍历所有主节点。
扫描完成后往往需要对大key进行治理。删除大key时不建议直接使用DEL,因为Redis单线程执行删除时会释放大量内存,容易造成数十毫秒甚至更久的阻塞。Redis 4.0及以上版本提供了UNLINK命令,它将内存回收放到后台线程异步处理,对主线程影响更小。示例:
redis-cli UNLINK task:queue:backup
除了删除,还可以考虑对大key进行拆分。比如将一个大哈希按照用户ID或日期拆成多个小哈希,或者把大字符串压缩后存储,或者设置合理的过期时间让系统自动回收。对于列表和有序集合,可以从业务侧限制最大长度,新增数据时使用LTRIM或ZREMRANGEBYRANK裁剪。
如果你需要更详细的内存分析,--bigkeys并不是唯一选择。开源工具redis-rdb-tools可以离线解析RDB快照文件,生成本文所有键的大小分布报告,不会对线上实例造成任何影响。云厂商Redis也通常提供大key分析和热key分析功能,监控页面可以直接看到内存占用最高的键。无论使用哪种方式,核心目标都是先定位问题,再制定低风险的拆分或删除方案。
总结来说,redis-cli --bigkeys是一个轻量、快速、容易上手的扫描工具,适合在紧急排查时快速缩小问题范围。理解它的扫描原理和限制,并根据实例规模合理设置扫描间隔,才能在不影响业务的前提下做好大key治理。