redis-cli --bigkeys 可快速定位拖慢 bgsave 的大Key,它按类型统计最大Key并显示 serializedlength;配合 MEMORY USAGE 验证实际内存占用,>1MB需警惕,>10MB基本确认为拖累源。

用 redis-cli --bigkeys 快速定位真正拖慢 bgsave 的大Key
大Key不光是“看起来大”,而是实际在持久化时让 bgsave fork 子进程卡住、写盘变慢的那些。最直接的判断方式,不是猜,是扫描——redis-cli --bigkeys 会按数据类型分组统计,告诉你哪个 hash 有 8 万字段、哪个 zset 有 20 万成员,这些才是 RDB 持久化的重灾区。
- 它默认只扫每个类型里“最大”的那个 Key,想更全一点,加
-i 0.01参数降低扫描频率,减少对线上影响 - 别在高峰期跑,尤其不要在 AOF 重写或
bgsave正在进行时执行,否则双重阻塞 - 输出里的
serializedlength(来自DEBUG OBJECT)比STRLEN或HLEN更准——它反映的是实际序列化后写入 RDB 的大小,含编码开销和元数据
拆分大Key必须避开 DEL 和全量 HGETALL 这类阻塞操作
发现一个 50MB 的 hash 后,直接 DEL 它?不行。Redis 主线程会卡死几十毫秒甚至上百毫秒,期间所有请求排队,QPS 断崖下跌。同理,HGETALL 拉全量再重写,网络+内存双压,极易触发 OOM。
- 用
HSCAN分批读:每次HSCAN key cursor COUNT 100,游标推进,避免单次加载全部 - 新 Key 命名带分片标识,比如
user:profile:12345:chunk:001,后续查的时候用 pipeline 并行取 - 删除老 Key 用
UNLINK替代DEL:它把释放内存交给后台线程,主线程几乎不阻塞 - 拆分过程务必放在业务低峰期,且监控
latency latest和info stats中的total_commands_processed是否骤降
MEMORY USAGE 比想象中更关键:它决定 bgsave fork 的页表复制耗时
很多人以为大Key只是“写盘慢”,其实更致命的是 fork 阶段:Redis 创建子进程做 RDB 时,要复制父进程的页表。如果一个 Key 占用 64MB 内存,它可能分散在数百个内存页里,fork 就得拷贝这几百个页表项——哪怕 Key 本身没被修改,这个复制动作也卡主线程。而 MEMORY USAGE key 返回的字节数,就是内核实际分配给该 Key 的内存块大小,直接关联 fork 开销。
- 对疑似大Key,先跑
MEMORY USAGE key,>1MB 就该警惕;>10MB 基本确认是bgsave拖累源 - 配合
INFO memory看mem_fragmentation_ratio,如果 >1.5 且大Key集中,说明碎片加剧了 fork 成本 - 别依赖
DEBUG OBJECT key的refcount或encoding判断是否安全删——refcount=1 不代表没被其他命令引用中,encoding 只反映当前结构,不反映实际内存占用
AOF 策略选 everysec 不是万能解,大Key 写入仍会卡主线程
很多人改 AOF 为 appendfsync everysec 就以为高枕无忧,但忽略了一个关键点:Redis 主线程要把写命令先写进 AOF 缓冲区,这个动作是同步的。如果一条 HSET 往一个百万成员的 hash 里塞值,命令本身序列化就耗时,缓冲区写入阶段就会阻塞主线程——跟磁盘 fsync 无关。
- 大Key 的写操作尽量避免高频变更,比如用定时聚合代替实时累加
- 如果必须更新,优先走
HMSET批量写,比 N 次HSET减少命令解析和缓冲区写入次数 - 禁用
auto-aof-rewrite-percentage的默认值(100%),设成 200% 或更高,避免大Key 导致 AOF 文件稍大就频繁触发重写,反复 fork - 重写期间,如果发现
bgrewriteaof_child_pid长时间存在,用INFO stats查latest_fork_usec,超 500ms 就说明页表复制已成瓶颈
MEMORY USAGE 验证和 UNLINK 替代 DEL 这两个动作。

















