基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

监控发现Redis Cluster中某个节点的内存使用率远高于其他节点(发生数据倾斜),如何排查并解决?

知识点图片

在Redis Cluster中发现某个节点内存使用率远高于其他节点(即发生数据倾斜内存倾斜),通常是由以下几个原因引起的:Slot(槽位)分配不均、存在大Key(Big Key)、Hash Tag滥用、或者非数据内存(如客户端缓冲区、内存碎片)占用过大。

以下是标准的排查步骤和对应的解决方案:


第一阶段:排查原因 (Troubleshooting)

1. 检查 Slot (槽位) 分配是否均匀

Redis Cluster将数据分布在16384个Slot中。如果某个节点分配的Slot数量远多于其他节点,自然会导致内存占用更高。

  • 排查命令
    bash
    redis-cli -c -h <ip> -p <port> cluster nodes
    # 或者
    redis-cli --cluster info <ip>:<port>
  • 观察:看每个Master节点负责的Slot数量是否大致相同(例如3个Master,每个应该在5461个左右)。

2. 检查 Key 的数量是否均匀

如果Slot分配是均匀的,但某个节点的Key数量远多于其他节点。

  • 排查命令
    登录到各个Master节点,执行:
    bash
    INFO keyspace
  • 观察:对比各个节点的 keys= 后面的数字。如果某个节点Key数量特别多,大概率是使用了 Hash Tag
  • Hash Tag 确认:检查业务代码,看是否大量使用了带有 {} 的Key(例如 user:{1001}:infouser:{1001}:orders)。Hash Tag 会强制把 {} 内相同字符串的Key路由到同一个Slot,导致某个Slot(进而导致某个节点)数据爆满。

3. 检查是否存在大 Key (Big Key)

如果Slot数量均匀,Key的总数也大致均匀,但内存倾斜严重,最常见的原因就是存在 Big Key(例如某个Hash、List、Set或Zset中包含了数百万个元素,或者某个String特别大)。

  • 排查方法 A:Redis自带工具 (对性能有轻微影响,建议低峰期执行)
    bash
    redis-cli -h <倾斜节点的IP> -p <port> --bigkeys
  • 排查方法 B:离线分析 RDB 文件 (最安全推荐)
    使用 redis-rdb-toolsRedisInsight 等工具,解析倾斜节点的 .rdb 备份文件,可以直观地导出内存占用最大的Key排行。
    bash
    rdb -c memory dump.rdb > memory.csv

4. 检查非数据内存占用 (缓冲区、内存碎片)

如果以上都没问题,可能是非数据本身占用的内存。

  • 客户端输出缓冲区 (Client Output Buffer):是否有消费极慢的客户端(如执行了大量的 MONITORKEYS *,或Pub/Sub消费慢),导致缓冲区堆积?
    bash
    INFO clients
    # 关注 client_recent_max_output_buffer 和 client_recent_max_input_buffer
    CLIENT LIST # 查看具体的 omem (输出缓冲区占用)
  • 内存碎片 (Fragmentation):节点频繁更新/删除数据,导致内存碎片率过高。
    bash
    INFO memory
    # 关注 mem_fragmentation_ratio,如果大于 1.5 说明碎片比较严重

第二阶段:解决方案 (Resolution)

根据上述排查出的根因,采取相应的解决措施:

场景 1:Slot 分配不均

解决办法:重新平衡集群的 Slot(Resharding)。
可以使用 Redis 官方工具重新分配 Slot,将倾斜节点上的 Slot 迁移一部分到空闲节点。

bash
# 自动平衡集群的 slot
redis-cli --cluster rebalance <ip>:<port> --cluster-use-empty-masters

# 或者手动指定迁移
redis-cli --cluster reshard <ip>:<port>

场景 2:存在大 Key (Big Key)

解决办法

  1. 业务拆分:联系研发,将大Key拆分成多个小Key。例如,一个包含1000万字段的Hash,通过哈希取模拆分成10个或100个Hash (hash:01, hash:02...),这样它们就会分散到不同的Slot中。
  2. 安全清理:如果大Key是废弃数据,千万不要直接用 DEL 删除(会阻塞Redis),应使用 UNLINK 异步删除;如果是大Hash/Set,建议通过脚本使用 HSCAN/SSCAN 逐步 HDEL/SREM 删除。

场景 3:Hash Tag 滥用

解决办法

  1. 评估业务逻辑:研发团队需要评估是否真的需要在一个事务中操作这些Key(只有需要用到事务或Lua脚本保证原子性时,才必须把Key放在同一个Slot)。
  2. 去除/修改 Hash Tag:如果不需要事务支持,去掉 {},让Key根据自身的完整名字自然分散到各个Slot去。

场景 4:客户端缓冲区堆积

解决办法

  1. 杀掉导致堆积的异常客户端连接 (CLIENT KILL)。
  2. 修改 redis.conf 中的配置,限制输出缓冲区大小,强制断开慢客户端:
    plaintext
    client-output-buffer-limit normal 0 0 0
    client-output-buffer-limit replica 256mb 64mb 60
    client-output-buffer-limit pubsub 32mb 8mb 60
  3. 排查业务侧是否有慢查询或拉取全量数据的操作(如滥用 HGETALL 等)。

场景 5:内存碎片过高

解决办法

  1. 开启自动碎片清理(Redis 4.0+ 支持):
    bash
    CONFIG SET activedefrag yes
  2. 重启节点:如果版本较低或碎片清理太慢,可以通过主从切换(Failover),将倾斜节点变为Slave,然后重启该节点,重启时会重新加载RDB,内存碎片自然消除。

第三阶段:预防建议 (Best Practices)

  1. 开发规范:建立严格的 Redis 开发规范,禁止在生产环境产生超过 10MB 的 String,或元素超过 10000 个的 Hash/Set/List/Zset;严格审批 Hash Tag 的使用。
  2. 监控告警:在 Prometheus/Grafana 等监控系统中,配置集群各节点内存使用率的方差告警(当某节点内存使用率偏离平均值超过20%时触发告警)。
  3. 定期巡检:定期执行离线 RDB 分析,提前发现有增长趋势的潜在大 Key,防患于未然。
00:00
00:00