news 2026/10/30 4:51:46

一次 redis 幽灵内存的分析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
一次 redis 幽灵内存的分析

一、背景

业务使用的 Redis 由主从切换到了 Cluster 后,Redis 出现了诡异的内存上涨。

二、排查路径

1.查看 info

查看 info 观测内存使用就是很高且碎片率很低,而且 key 的数量很少

info memory

2.查看真实数据大小

用 rob 等工具分析真实大小,详见文档,发现真实数据只有不到 1M

3.解析内存真实使用情况

用 gdb 分析 内存真实使用情况,注意在低峰期分析

3.1 编写 gdb 脚本

cat>/tmp/sndbuf.gdb<<'EOF' set pagination off set print elements 0 set confirm off set $t = server.cluster->nodes set $tab = $t->ht[0].table set $size = $t->ht[0].size set $i = 0 set $total_alloc = (unsigned long)0 set $total_len = (unsigned long)0 set $link_count = 0 printf "\n=== Cluster Link sndbuf ===\n" printf "%-42s %-22s %-6s %-14s %-14s\n", "NODE_NAME", "IP:CPORT", "TYPE", "SDS_LEN", "SDS_ALLOC" while $i < $size set $e = $tab[$i] while $e != 0 set $n = (clusterNode*)$e->v.val if $n->link != 0 if $n->link->sndbuf != 0 set $s = (unsigned char*)$n->link->sndbuf set $flags = *($s - 1) set $type = $flags & 7 set $len = (unsigned long)0 set $alloc = (unsigned long)0 if $type == 0 set $len = $flags >> 3 set $alloc = $len end if $type == 1 set $len = *(unsigned char*)($s - 3) set $alloc = *(unsigned char*)($s - 2) end if $type == 2 set $len = *(unsigned short*)($s - 5) set $alloc = *(unsigned short*)($s - 3) end if $type == 3 set $len = *(unsigned int*)($s - 9) set $alloc = *(unsigned int*)($s - 5) end if $type == 4 set $len = *(unsigned long*)($s - 17) set $alloc = *(unsigned long*)($s - 9) end printf "%-42s %s:%-6d type%d %-14lu %-14lu\n", $n->name, $n->ip, $n->cport, (int)$type, (unsigned long)$len, (unsigned long)$alloc set $total_alloc = $total_alloc + $alloc set $total_len = $total_len + $len set $link_count = $link_count + 1 end end set $e = $e->next end set $i = $i + 1 end printf "\n=== Summary ===\n" printf "Links: %d\n", $link_count printf "Total LEN: %lu bytes (%.2f MB)\n", $total_len, ((double)$total_len) / 1048576.0 printf "Total ALLOC: %lu bytes (%.2f MB)\n", $total_alloc, ((double)$total_alloc) / 1048576.0 detach quit EOF

3.2 执行脚本分析

# 639587 为 redis 进程号sudotimeout3gdb-p639587-batch-x/tmp/sndbuf.gdb2>&1

3.3 结果分析

LEN = 0:所有链接当前没有积压待发送数据,发送通道是通的,网络 / 对端消费正常。
ALLOC = 406MB:但缓冲区曾经膨胀过,并且没有被回收:
这是 Redis Cluster 的一个典型内存"隐性膨胀"问题:

  1. SDS 容量只增不减
    Redis 的 sds 在 sdsclear() / 数据发送完后,只会把 len 置 0,不会释放已分配的 buf。所以一旦某瞬间堆积过大,内存就被永久占用(直到链接重建)。
[New LWP637685][New LWP637686][New LWP637687][New LWP637688][Thread debugging using libthread_db enabled]Usinghostlibthread_db library"/lib/x86_64-linux-gnu/libthread_db.so.1".0x00007fc5395b3e16inepoll_wait()from /lib/x86_64-linux-gnu/libc.so.6===Cluster Link sndbuf===NODE_NAME IP:CPORT TYPE SDS_LEN SDS_ALLOC 77ed8a2e0c39d2f2a304e1ad216b56e3e0664a3b192.168.1.1:16379 type3041943030ee29bbe0d05ccd9a64215631ee140ea27921c81b192.168.1.2:16379 type306291446347b2126df4bde7c5b1ebb10ff43a51d09535733192.168.1.3:16379 type30167772150a4980d05cd232dadab82e25e7429c2f50c433ab9192.168.1.4:16379 type3041943030394723213b2d62e0b6276dfbf4d5247b80d289b0192.168.1.5:16379 type30167772150===Summary===Links:5Total LEN:0bytes(0.00MB)Total ALLOC:425721806bytes(406.00MB)[Inferior1(process637684)detached]

3.4 详细原因排查

那么为什么会有这种现象呢,我们分析监控发现每次内存增长的时候都有一个 eval 的慢日志,然后我们通过慢日志发现如下命令
单条 publish 的 payload 接近 4.5MB,因为 Redis 7 以下是全量广播,所以会出现这个放大的问题。

│ 一次业务 EVAL 调用的放大链 │ │ │ │ 客户端发出:4.5MB │ │ ↓ Lua 内2次 publish │ │ Redis 处理:9MB │ │ ↓ cluster bus 广播到5个节点 │ │ 集群内传输:45MB │ │ ↓ 其中真有订阅者的可能只有1~2 个节点 │ │ 有效流量: ≤9MB │ │ 浪费流量: ≥36MB(80%!)│ └─────────────────────────────────────────────────────┘
  • 命令
4)1)"eval"2)"for i = 2, #ARGV do redis.call(\"publish\", ARGV[i], ARGV[1]) end"3)"0"4)"{\"src\":\"******\",\"seq\":5,\"v\":0,\"create\":{\"type\":\"******",\"data\":{\"tabs\":[\"0\",\"...(29959420morebytes)" 5) "******" 6) "*******"

三、解决方案

1.解决方案

  • 升级到 Redis 7.2.9
    因为 Redis 7 只广播到对应节点,且有 sds 回收机制,所以升级到 7 后内存不再有上升的情况

2.遇到的问题

第二天中午时出现业务告警,显示 command 命令不支持,这是因为 command 返回的数据格式有差异,低版本的 lettuce 处理不了这个问题,而 command 命令只有重连的时候会出现,所以第一天没有发现

3.如何解决和避免

  • 解决:业务 lettuce 版本由 5.1.8 升级 5.3.7,解决问题,能升级 6.2 以上更好,不过需要注意 sring boot 的版本
  • 避免:在测试的时候业务应该把 DBA 提供的访问 Redis 的服务代码用 AI 扫描下,这样就能避免不适配的命令
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/30 4:47:12

力扣543二叉树的直径(深度优先搜索法(递归))

543. 二叉树的直径 - 力扣&#xff08;LeetCode&#xff09; 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 即求路径经过节点数最大值 - 1 假设我们已知某节点左儿子向下遍历经过最多节点数L、右儿子向下遍历经过最…

作者头像 李华
网站建设 2026/10/30 4:44:54

MySQL 自增 ID 空洞:为什么删了数据 ID 还是不连续

个人主页&#xff1a;> for_ever_love__ <&#xff08;欢迎各位大佬莅临&#x1f60a;&#xff09; 其他栏目: > 大模型开发从0到1 < 其他栏目: > iOS项目总结大全 < 其他栏目: > 我想学python了 < 其他栏目: > iOS UI < 文章目录MySQL 自增 ID …

作者头像 李华
网站建设 2026/10/30 4:41:40

8 倍速清日常:HsMod 从装好到上手的完整指南

8 倍速清日常&#xff1a;HsMod 从装好到上手的完整指南 【免费下载链接】HsMod Hearthstone Modification Based on BepInEx 项目地址: https://gitcode.com/GitHub_Trending/hs/HsMod HsMod 是基于 BepInEx 5 的炉石传说改件&#xff1a;8 倍变速、空格连开 5 包、战棋…

作者头像 李华