Redis 的 ZipList 是什么?它是怎么解决内存碎片问题的?

📅 发布时间:2026/7/6 19:44:48 👁️ 浏览次数:
Redis 的 ZipList 是什么?它是怎么解决内存碎片问题的?
你们公司做了一款社交 App需要缓存 1000 万用户的“个性化配置”如夜间模式1通知开关0主题色blue。你理所当然地使用了 Redis 的Hash结构HSET user:10086 theme blue dark_mode 1 ...绝望时刻你预估这些数据加起来最多也就 500MB结果全量导入 Redis 后监控面板直接报警内存飙升了 4GB灵魂拷问为什么存这么点数据内存会膨胀近 10 倍答案就在于标准的 Hash 表里指针和元数据的开销太大了。想要省钱你必须触发 Redis 的隐藏被动技能——ZipList (压缩列表)。1. 核心痛点昂贵的“指针”与内存碎片在 64 位操作系统下一个标准的指针占用 8 个字节。如果你使用标准的双向链表LinkedList或哈希表HashTable来存数据每个节点需要prev指针8 字节和next指针8 字节。还要加上 Redis 自身的robj对象头。内存分配器还会产生内存碎片。如果你只存一个字符11 字节底层却为你分配了 40 多字节的包袱。这就像是为了运送一颗芝麻专门叫了一辆 18 轮的重型卡车。2. 破局原理ZipList 的“连续车厢”魔法ZipList (压缩列表)的核心思想就是干掉指针用连续内存排排坐它本质上是一个经过特殊编码的连续内存块。它把所有的元素像高铁车厢一样首尾相连紧紧贴在一起。没有前后指针它通过记录“前一个节点的长度”和“当前节点的长度”通过计算偏移量来寻找下一个数据。极度节省空间连长度字段都做了极致压缩如果长度小于 254 字节长度字段本身只占 1 个字节。3. 三大实战场景什么时候它最香ZipList 并不是一种独立的数据类型你不能SET ziplist ...它是List、Hash、ZSet这三种数据结构在数据量较小时的底层默认实现。实战场景一海量用户的短属性缓存 (Hash)业务存储千万级用户的设备信息、基础偏好设置字段少值短。收益将外层的大 Hash 拆分成多个内部包含小 Hash 的结构。只要保证每个 Hash 的元素个数不超过阈值默认 512 个Redis 就会在底层使用 ZipList 存储。内存占用通常能骤降 70% - 80%。实战场景二迷你排行榜 / 积分墙 (ZSet)业务比如微信步数排行榜只展示每个群里的 Top 100 活跃用户或者某个商品下按点赞数排序的最近 50 条短评。收益只要 ZSet 的元素数量不多底层就不会去建庞大的“跳表 (SkipList)”和“字典”而是用连续的 ZipList 存储。分数和成员交替挨着存省下了巨量的指针空间。实战场景三短消息流 / 历史足迹 (List)业务记录用户最近浏览的 50 个商品 ID或者最近的 20 条系统通知。收益老版本 Redis (3.2前) 纯靠 ZipList。虽然现在List类型底层统一升级成了QuickList但QuickList本质上也是由一个个ZipList节点串起来的。调优每个节点的大小依然能极大优化内存。4. 命运的代价拿 CPU 换内存天下没有免费的午餐。既然 ZipList 把数据挤压得这么紧修改起来自然非常痛苦。内存重分配 (Realloc)每次向 ZipList 插入或删除元素由于需要保证内存连续Redis 可能需要重新申请一块更大的内存把整个列表memmove拷贝过去。连锁更新 (Cascade Update)这是 ZipList 最臭名昭著的缺陷。因为每个节点都存了“前一个节点的长度”如果你突然插入一个极大的元素导致当前节点长度字段为了记录它而膨胀比如从 1 字节变成 5 字节这可能会引发多米诺骨牌效应导致后续所有节点的长度字段跟着膨胀引发连续的内存重分配导致 CPU 瞬间飙升。因此ZipList 只有在“数据量小”且“元素体积小”时才是一个好东西。5. 如何触发与调优(黄金参数)在redis.conf中你可以通过调整以下参数来控制何时使用 ZipList# Hash 类型转换为字典的阈值 (默认元素超过 512 个或单个值大于 64 字节就放弃 ZipList) hash-max-ziplist-entries 512 hash-max-ziplist-value 64 # ZSet 类型的转换阈值 zset-max-ziplist-entries 128 zset-max-ziplist-value 64实战调优建议如果你的服务器 CPU 性能过剩但内存极度紧张比如云上极其昂贵的内存实例你可以适当把entries调大比如调到 1000用 CPU 的计算时间去换取宝贵的 RAM 空间。6. 现实修正Redis 7.0 的进化作为事实的补充为了解决上面提到的“连锁更新”这个致命 CPU 缺陷Redis 从 7.0 版本开始已经全面废弃了 ZipList替换成了全新的Listpack(紧凑列表)。Listpack 的改进它依然保持了连续内存的极致压缩特性省内存的效果一样好但它去掉了“记录前一个节点长度”的设计彻底根除了“连锁更新”的雪崩风险。对开发者的影响架构思想完全一致。你在配置里看到的hash-max-ziplist-entries等参数在底层依然控制着向Listpack的转换。理解了 ZipList就完全理解了现代 Redis 小数据存储的核心哲学。总结Redis 并不是一味的傻快它在内部极其精打细算。当数据量大时它用 HashTable 和 SkipList 保证O(1) 或 O(logN) 的绝对速度。当数据量小时它用 ZipList / Listpack 榨干内存因为在极小的数据集上连续内存遍历的速度CPU Cache 命中率高甚至比 Hash 查找还要快