Redis 从 0 到 1(二十六):INFO memory 里 8 个关键数字——内存构成与容量规划
发布时间:2026/11/6 3:31:18 锦皓数字建站
:INFO memory 里 8 个关键数字——内存构成与容量规划`)
我不会起名字322· 后端 / 算法 / 数据库 技术栈 力扣 Hot100 Go 项目 Redis MySQL文章目录Redis 从 0 到 1二十六INFO memory 里 8 个关键数字——内存构成与容量规划一、现象数据 1Gused_memory 2G二、INFO memory 的 8 个关键字段收藏点 1三、编码转换同一份数据内存差 5 倍四、Go 实操编码转换的内存测量收藏点 2五、容量规划公式收藏点 3六、排查步骤内存超标时按顺序看七、总结Redis 从 0 到 1二十六INFO memory 里 8 个关键数字——内存构成与容量规划上一篇《Redis 从 0 到 1二十五连接池又打满了——PoolSize 公式、泄漏排查与 PoolStats》我们把连接池调成了理性状态链接https://blog.csdn.net/2501_92769340/article/details/167399911 。本篇往内存里看Redis 的内存到底花在哪了怎么规划才不被半夜的 OOM 叫醒。一、现象数据 1Gused_memory 2G运维同学扩容前做容量核对INFO memory显示 used_memory 2.1GB而业务自己估算的数据量只有 1GB。“另一半被谁吃了”答案通常藏在四个口袋里数据结构开销、编码差异、缓冲区、碎片。本篇把这四个口袋逐个拆开给每个数字一个名字。二、INFO memory 的 8 个关键字段收藏点 1字段含义关注点used_memoryRedis 自己记账的数据结构总字节容量规划基准admin 视角used_memory_rss操作系统视角实际占用含碎片、页缓存与 used_memory 的比值见下行mem_fragmentation_ratioRSS / used_memory1.0~1.1 健康1.5 警惕碎片第 30 篇1.0 说明 swap 或共享内存used_memory_dataset纯数据部分业务真实数据量used_memory_overhead结构开销dictEntry、redisObject、过期字典overhead/dataset 比例反映 key 数量级开销used_memory_luaLua 引擎占用本系列不用 Lua此项很小异常增长要查mem_clients_normal / mem_clients_slaves客户端输出缓冲slave 缓冲大 复制卡了第 8 篇来源 3used_memory_startup启动基础开销常数无需关注经验诊断used_memory 涨但 used_memory_dataset 不涨→ 结构开销在涨key 太多或大量空结构overhead 占一半以上→ key 设计有问题短 key 千万级时 overhead 能超过数据本身第 4 篇的 key 长度规范就是为它定的rss used_memory→ 碎片下一篇。三、编码转换同一份数据内存差 5 倍Redis 每种结构都有紧凑编码和普通编码两档按元素数量/大小自动转换结构紧凑编码转换阈值redis.conf 可调普通编码Hashlistpackhash-max-listpack-entries 128、hash-max-listpack-value 64hashtableZSetlistpackzset-max-listpack-entries 128skiplistListlistpacklist-max-listpack-size 128quicklistSetintsetset-max-intset-entries 512hashtable含义一个 100 个 field 的小 Hash 走 listpack内存接近纯数据第 129 个 field 触发转换整体搬迁成 hashtable内存瞬间翻几倍。同一份数据128 个 field 和 129 个 field 的内存差可能是 3 倍——这就是数据明明一样多内存却对不上的常见原因。OBJECT ENCODING user:1001# 看当前编码CONFIG GET hash-max*# 看阈值四、Go 实操编码转换的内存测量收藏点 2packagemainimport(contextfmttimeredis-0to1/redisx)funcmain(){rdb,err:redisx.NewClient(redisx.Config{Addr:127.0.0.1:6379,Password:redis123,PoolSize:10})iferr!nil{panic(err)}ctx:context.Background()// 对照实验128 个 fieldlistpackvs 200 个 fieldhashtable同 field 大小measure:func(nint){key:fmt.Sprintf(memtest:%d,n)pipe:rdb.Pipeline()fori:0;in;i{pipe.HSet(ctx,key,fmt.Sprintf(f%03d,i),value-of-same-length-32bytes)}pipe.Exec(ctx)enc,_:rdb.ObjectEncoding(ctx,key).Result()usage,_:rdb.MemoryUsage(ctx,key,0).Result()fmt.Printf(fields%d 编码%s 内存%d 字节\n,n,enc,usage)rdb.Del(ctx,key)}measure(128)// 典型结果编码listpack约 5KBmeasure(200)// 典型结果编码hashtable约 15KB3 倍差}数字随版本略有出入量级不变转换点前后内存差数倍。五、容量规划公式收藏点 3实例容量规划 1. 数据量 单条平均字节 × 条数 × 结构系数listpack 小对象约 1.2hashtable 约 2~4 2. 加 key 开销key 数 × dictEntryredisObject ≈ 100 字节 3. 加缓冲区余量AOF 重写缓冲区 复制缓冲 ≈ 数据量的 10%~20% 4. 加碎片余量RSS used_memory × 1.1~1.3第 30 篇 5. maxmemory 设为物理内存的 70%给 fork 留出 COW 空间第 7 篇 RDB 需要 例1 亿 key、单 value 500B、平均 3 个 field 的 Hash → 数据 50GB × 结构系数 2 key 开销 10GB 缓冲 12GB ≈ 122GB → 实例物理内存 ≥ 122GB / 0.7 ≈ 175GB → 一台装不下就上 Cluster 分片六、排查步骤内存超标时按顺序看INFO memory拍全景dataset vs overhead vs rss 三分法定位大类MEMORY USAGE抽查可疑大 key第 22 篇OBJECT ENCODING抽查高频 key 是否全在 hashtable阈值被业务数据形态踩穿对照公式重算容量规划决定扩内存还是拆 Cluster。七、总结used_memory ≠ 数据量结构开销、编码、缓冲、碎片四个口袋编码转换点listpack→hashtable是内存的隐形台阶阈值可配可调可观测容量规划五步法数据 × 结构系数 key 开销 缓冲 碎片 maxmemory 打 7 折小 Hash/小 ZSet 保持 listpack 是免费的内存优化。下一篇预告内存省了一半网络传输还能再省——下一篇《JSON 体积是 Protobuf 的 3.7 倍3 种序列化的 Go 基准测试》用 Benchmark 数据说话选对你的 Codec。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。