不能直接在生产主节点高流量时段运行redis-cli --bigkeys,因其全量scan遍历加type及大小探测会显著抬升cpu与网络负载,易拖慢请求;应优先选从节点、加-i参数限速、避开高峰,并配合memory usage精准识别真实内存大户。

redis-cli --bigkeys 能不能直接用于生产环境扫描
不能直接在主节点高流量时段跑 redis-cli --bigkeys。它本质是用 SCAN 遍历所有 key,再对每种类型调用 LLEN、HLEN、SCARD 等命令——这些操作本身不阻塞,但高频调用会显著抬升 CPU 和网络负载,尤其当 key 数量超百万时,容易拖慢正常请求。
实操建议:
- 优先在从节点执行,避免影响主节点响应延迟
- 加
-i 0.1参数(每 100 次SCAN后休眠 100ms),降低扫描节奏 - 避开业务高峰,选凌晨或维护窗口;若必须白天扫,先用
INFO stats看instantaneous_ops_per_sec是否低于日常均值 30% - 注意:它只返回每种类型(string/hash/list/set/zset)中“最大”的那个 key,不是 Top N,也**不反映实际内存占用**——比如一个含 1 万个短字符串的
SET可能比一个含 500 个长 JSON 的HASH占内存更少,但--bigkeys会优先标出前者
MEMORY USAGE 比 --bigkeys 更准,但怎么批量用
MEMORY USAGE 返回的是近似内存字节数,对识别真正的大 value(比如单个 string > 1MB、hash 总大小 > 10MB)更可靠。但它不能直接 SCAN,必须先拿到 key 名再逐个查,暴力遍历全库会卡住。
安全批量方案:
- 用
SCAN分批拉 key:SCAN 0 COUNT 500,每次取 500 个,自己维护游标 - 对每个 key 调
MEMORY USAGE,捕获异常(如 key 不存在、权限不足)并跳过,别让脚本中断 - 阈值按业务定:string 类型超过
1024 * 1024(1MB),hash/list/set 超过10 * 1024 * 1024(10MB)就记为大 key - Python 示例里别用
r.scan_iter(count=100)直接遍历全库——它底层仍是一次性拉完游标,在大数据集上可能 OOM;改用手动游标循环 + 限流(如每秒最多 20 次MEMORY USAGE)
发现大 Key 后,为什么不能直接 DEL,该用 UNLINK
DEL 是同步删除:Redis 主线程要逐个释放 key 占用的所有内存页,遇到几 MB 的 string 或上万 field 的 hash,可能卡住几十毫秒甚至更久,导致后续命令排队、超时、连接堆积。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
UNLINK 把释放内存的动作交给后台 lazyfree 线程,主线程只解引用,毫秒级返回。但前提是 Redis 配置开了 lazyfree-lazy-user-del yes(4.0+ 默认开启)。
关键点:
- 确认配置:
CONFIG GET lazyfree-lazy-user-del返回["lazyfree-lazy-user-del", "yes"]才有效 -
UNLINK对KEYS命令无效,只能删单个 key;批量删要用 Lua 脚本封装或客户端循环调用 - 删之前务必确认 key 是否被业务强依赖——比如用户 session 大 hash 删了会导致登录态丢失,得配合业务灰度下线
拆分大 Hash/List 时,如何避免读写不一致
典型场景:一个 user:1001:profile hash 存了 8000 个字段,想拆成 user:1001:profile:1 ~ user:1001:profile:4 四个子 key。难点不在拆,而在拆过程中老 key 还在写、新 key 还没写全,读请求可能读到脏数据。
稳妥做法是三阶段迁移:
- 第一阶段(读双写单):新逻辑写四个子 key,老逻辑仍只读原 key;同时后台任务把原 key 数据逐步迁移到子 key 中
- 第二阶段(读双写双):读逻辑改成先查子 key,查不到再查原 key;写逻辑同步更新原 key 和子 key,保证两者一致
- 第三阶段(读单写单):确认子 key 数据完整、无写入延迟后,停写原 key,删掉它
整个过程需要业务代码配合,不能只靠 Redis 命令完成;另外,拆分粒度别太细——比如按 field 名哈希模 4,比按顺序切片更利于未来扩容。










