Redis BigKey

一、定义

1. 字符串(String)

  • Value 大小 ≥ 10KB

2. 集合类(Hash/List/Set/ZSet)

  • 元素数量 ≥ 5000(或单 Key 内存占用 ≥ 1MB)
    • 可通过 MEMORY USAGE <key> 精准获取 Key 内存占用

二、业务场景

1. 日志 / 数据缓存

  • 将全量业务日志、批量数据(如订单列表)一次性存入单个 Key

2. 未拆分的业务数据

  • 用户全量收货地址、商品全量属性存入单个 Hash Key

3. 消息队列积压

  • List 作为消息队列时,消费速度低于生产速度,导致元素堆积成 BigKey

4. 批量导入未分片

  • 数据迁移 / 导入时,未按规则拆分,直接将全量数据写入单个 Key

5. 缓存大文件

  • 将图片、PDF 等二进制文件以 String 类型存入 Redis(违背 Redis 设计初衷)

三、风险

1. 读取 / 写入耗时

  • BigKey 序列化 / 反序列化占用主线程,导致请求延迟飙升(P99 超时)

2. 内存碎片

  • BigKey 频繁修改易产生内存碎片,降低 Redis 内存利用率

3. 过期 / 删除阻塞

  • DEL 命令删除 BigKey 会阻塞主线程(Redis 8.4 仍未解决,需用 UNLINK)

4. 主从同步延迟

  • BigKey 同步占用网络带宽,导致主从节点数据不一致

5. 集群槽位倾斜

  • 单个 BigKey 落在某节点,导致该节点内存 / CPU 负载远超其他节点

四、常用操作

1. 生产环境危险命令

  • keys / flushdb / flushall

2. 配置 redis.conf 禁用危险命令

# rename-command CONFIG ""
rename-command keys ""
rename-command flushdb ""
rename-command flushall ""

3. 生产环境遍历元素

  • SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]

    • SCAN 遍历键,是一个基于游标的迭代器(O(1))
    127.0.0.1:6379> scan 0 match k* count 2
    1) "3"
    2) 1) "k3"
       2) "k4"
    127.0.0.1:6379> scan 3 match k* count 2
    1) "0"
    2) 1) "k2"
       2) "k1"
    
  • HSCAN/SSCAN/ZSCAN key cursor [MATCH pattern] [COUNT count] [NOVALUES]

    • HSCAN 遍历 hash
    • SSCAN 遍历 set
    • ZSCAN 遍历 zset

五、注意事项

1. 预防层面

(1)数据拆分

  • 字符串:按业务维度拆分(如用户日志按天拆分为 user:log:20251230、user:log:20251231)
  • 集合类:Hash 按字段范围拆分(如 user:info:1000-2000),List/Set 按数量分片

(2)规范写入

  • 禁止直接存储大文件(图片 / 视频),改用分布式文件存储(如 MinIO)+ Redis 存 URL
  • 批量写入时限制单次元素数量(如 Hash 单次 HMSET 不超过 1000 字段)

(3)监控预警

  • 定期扫描 BigKey:使用 redis-cli --bigkeys 或第三方工具(如 RedisInsight)

    [root@yingzai single]#redis-cli -p 6379 -a 12345678 --bigkeys
    Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
    
    # 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 SCAN commands (not usually needed).
    
    100.00% ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
    Keys sampled: 4
    
    -------- summary -------
    
    Total key length in bytes is 8 (avg len 2.00)
    
    Biggest string found "k3" has 2 bytes
    
    0 lists with 0 items (00.00% of keys, avg size 0.00)
    0 hashs with 0 fields (00.00% of keys, avg size 0.00)
    0 streams with 0 entries (00.00% of keys, avg size 0.00)
    4 strings with 8 bytes (100.00% of keys, avg size 2.00)
    0 sets with 0 members (00.00% of keys, avg size 0.00)
    0 zsets with 0 members (00.00% of keys, avg size 0.00)
    
  • 配置内存告警:对单个 Key 内存占用设置阈值(如 100KB),触发告警及时处理

    • Redis 8.4 优化了 MEMORY USAGE 命令性能,可高频检测 Key 内存占用,无显著性能损耗

(4)过期策略

  • 避免 BigKey 设置永不过期,按业务周期设置过期时间(如 7 天),减少长期占用

2. 处理层面

(1)删除操作(渐进式删除)

  • 禁用 DEL 命令删除 BigKey,改用 UNLINK(异步删除,Redis 8.4 默认支持,后台线程执行)
  • 超大集合类 BigKey:分批删除,避免单次操作阻塞
    • String 直接 del
    • List 使用 trim 截取
    • Hash 用 HSCAN 遍历字段,每次读取少量,逐个 HDEL

(2)读取操作

  • 避免全量读取 BigKey(如 SMEMBERS、HGETALL),改用分段读取(SSCAN、HSCAN、LRANGE 分页)
  • 大字符串读取:按需截取(如 GETRANGE),而非一次性读取全量

(3)集群适配

  • BigKey 拆分后均匀分布到不同槽位,避免槽位倾斜
  • 禁止在集群中使用跨槽位的 BigKey(如 Hash Tag 导致的单槽位 BigKey)

(4)惰性释放

  • 开启 lazy-free 惰性释放配置(lazyfree-lazy-del yes),进一步降低 BigKey 删除的阻塞风险

3. 应急层面

  • BigKey 导致主线程阻塞:立即暂停该 Key 的读写,切换到备用 Key 提供服务,低峰期分批清理
  • 主从同步延迟:临时降低该 BigKey 的写入频率,或手动触发部分同步(PSYNC)
  • 内存溢出风险:优先淘汰非核心 BigKey,或扩容 Redis 节点内存,避免 OOM

六、面试题

1. Redis 的 BigKey、MoreKey、HotKey 问题,如何解决?

(1)概念对比

image

(2)HotKey

1)危害
  • 单节点瓶颈:Redis 是单线程处理命令,单个热键的高频访问会占满该节点的 CPU,导致其他请求阻塞
  • 网络拥塞:热键的大量请求会耗尽节点的网络带宽,引发延迟飙升
  • 主从同步压力:热键的频繁修改会导致主从复制流量过大,从库同步延迟
  • 缓存击穿 / 雪崩:若热键过期 / 失效,大量请求会穿透到数据库,压垮后端服务
2)如何检测
  • redis-cli --hotkeys:内置的热键检测工具,通过扫描键空间统计访问频率(缺点:会阻塞主线程,生产环境慎用)

    redis-cli --hotkeys -h {host} -p {port} -a {password}
    
  • INFO commandstats:统计各命令的执行次数,结合业务判断热键(如 GET key1 次数远超其他)

    redis-cli INFO commandstats | grep -E "cmdstat_get|cmdstat_hget"
    
3)解决方案
  • 核心思路:分散热键的访问压力
  • 具体:image

(3)MoreKey

1)危害
  • 内存耗尽:大量键占用内存,触发 Redis 内存淘汰策略(如 LRU),导致正常数据被淘汰
  • 持久化卡顿:RDB 快照生成时需要遍历所有键,键数量过多会导致 fork 耗时、快照文件过大,AOF 重写同理
  • 扫描类命令阻塞:KEYS *、SCAN 等命令遍历大量键,阻塞 Redis 主线程
  • 扩容迁移慢:Redis Cluster 扩容时,迁移大量键会占用网络和 CPU,导致服务抖动
  • 运维成本高:大量键的管理、监控、排查问题难度剧增
2)如何检测
  • INFO keyspace:查看各数据库的键总数

    [root@yingzai single]#redis-cli -p 6379 -a 12345678 info keyspace
    Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
    # Keyspace
    db0:keys=4,expires=0,avg_ttl=0,subexpiry=0
    
  • dbsize:快速获取当前数据库的键数量

    redis-cli dbsize
    
  • SCAN 遍历统计:非阻塞遍历所有键,统计总数(适合超大量键)

    redis-cli --scan | wc -l
    
3)解决方案
  • 核心思路:减少键的数量、合理分片、生命周期管理
  • 具体:image

2. 什么是 Redis BigKey?生产环境中如何定义 BigKey?

  • 见上文“定义”部分

3. Redis BigKey 会带来哪些问题?为什么?

  • 主线程阻塞:BigKey 序列化 / 删除耗时,Redis 单线程处理命令,会导致所有请求排队
  • 内存碎片:BigKey 频繁修改易产生内存碎片,降低内存利用率
  • 主从同步延迟:同步 BigKey 需传输大量数据,占用带宽且序列化耗时
  • 集群槽位倾斜:单个 BigKey 集中在某节点,导致该节点负载过高
  • 网络带宽占用:BigKey 读写需传输大量数据,消耗网络资源
  • DEL 是同步删除(阻塞主线程)
  • UNLINK 是异步删除(后台线程执行,仅标记 Key 待删除)

5. 为什么 BigKey 会导致 Redis 主从同步延迟?如何解决?

(1)延迟原因

  • 主节点同步 BigKey 需将全量数据通过网络发送给从节点,占用带宽且序列化耗时

(2)解决

  • 拆分 BigKey、降低写入频率、使用增量同步、临时关闭该 Key 的同步(低峰期补数据)等

6. 生产环境中发现一个 50w 元素的 Set 类型 BigKey,如何安全删除?

  • 确认该 Key 无业务读写(或临时切换备用 Key)
  • 禁用 DEL 命令,优先用 UNLINK
  • 若 UNLINK 仍有风险,用 SSCAN 分批遍历元素,逐个 SREM 删除
  • 清理后监控内存释放和主线程阻塞情况
  • 复盘产生原因,优化写入逻辑

7. 如何避免业务代码中产生 BigKey?给出具体的技术方案

  • 数据拆分:按时间 / 范围 / 哈希分片
  • 写入规范:禁止存大文件、批量写入限流
  • 监控预警:定期扫描 + 阈值告警
  • 过期策略:避免永不过期

8. 集群环境中 BigKey 导致某节点内存使用率 90%,其他节点仅 30%,如何处理?

  • 紧急处理:将该 BigKey 拆分,迁移部分数据到其他节点
  • 短期优化:调整集群槽位映射,均衡节点负载
  • 长期优化:规范 Key 命名和分片规则(如 Hash Tag 避免单槽位堆积),完善 BigKey 监控和自动拆分机制

9. Redis BigKey 和 HotKey 有什么区别?处理方式有哪些不同?

(1)区别

  • 定义:BigKey 是内存 / 元素量大,HotKey 是访问频率高
  • 影响:BigKey 影响内存 / 阻塞,HotKey 影响 CPU / 带宽

(2)处理方式

  • BigKey 侧重拆分 / 异步删除
  • HotKey 侧重缓存分片 / 读写分离

10. 为什么不建议用 Redis 存储大文件(如 1MB 以上的图片)?有什么替代方案?

  • 原因:Redis 设计初衷是内存缓存,大文件会占用大量内存且读写耗时
  • 替代方案:用分布式文件存储(MinIO/OSS)存储文件,Redis 仅存储文件 URL 和元数据