Kaito
2021-08-07
1、ziplist 设计的初衷就是「节省内存」,在存储数据时,把内存利用率发挥到了极致: - 数字按「整型」编码存储,比直接当字符串存内存占用少 - 数据「长度」字段,会根据内容的大小选择最小的长度编码 - 甚至对于极小的数据,干脆把内容直接放到了「长度」字段中(前几个位表示长度,后几个位存数据) 2、但 ziplist 的劣势也很明显: - 寻找元素只能挨个遍历,存储过长数据,查询性能很低 - 每个元素中保存了「上一个」元素的长度(为了方便反向遍历),这会导致上一个元素内容发生修改,长度超过了原来的编码长度,下一个元素的内容也要跟着变,重新分配内存,进而就有可能再次引起下一级的变化,一级级更新下去,频繁申请内存 3、想要缓解 ziplist 的问题,比较简单直接的方案就是,多个数据项,不再用一个 ziplist 来存,而是分拆到多个 ziplist 中,每个 ziplist 用指针串起来,这样修改其中一个数据项,即便发生级联更新,也只会影响这一个 ziplist,其它 ziplist 不受影响,这种方案就是 quicklist: qucklist: ziplist1(也叫quicklistNode) <-> ziplist2 <-> ziplist3 <-> ... 4、List 数据类型底层实现,就是用的 quicklist,因为它是一个链表,所以 LPUSH/LPOP/RPUSH/RPOP 的复杂度是 O(1) 5、List 中每个 ziplist 节点可以存的元素个数/总大小,可以通过 list-max-ziplist-size 配置: - 正数:ziplist 最多包含几个数据项 - 负数:取值 -1 ~ -5,表示每个 ziplist 存储最大的字节数,默认 -2,每个ziplist 8KB ziplist 超过上述任一配置,添加新元素就会新建 ziplist 插入到链表中。 6、List 因为更多是两头操作,为了节省内存,还可以把中间的 ziplist「压缩」,具体可看 list-compress-depth 配置项,默认配置不压缩 7、要想彻底解决 ziplist 级联更新问题,本质上要修改 ziplist 的存储结构,也就是不要让每个元素保存「上一个」元素的长度即可,所以才有了 listpack 8、listpack 每个元素项不再保存上一个元素的长度,而是优化元素内字段的顺序,来保证既可以从前也可以向后遍历 9、listpack 是为了替代 ziplist 为设计的,但因为 List/Hash/Set/ZSet 都严重依赖 ziplist,所以这个替换之路很漫长,目前只有 Stream 数据类型用到了 listpack
展开
共 6 条评论
41
lzh2nix
2021-08-15
从ziplist--->quickList这在计算机里也是一个常见的设计模式。 为了防止单个数据表太大,我们将其拆分成多个数据表,也叫 [分桶设计],数据库中的sharding,以及将一个大粒度的锁拆分成多个小粒度的锁都是类似的思想。
11
一步
2021-08-08
listpack 解决了 ziplist 连锁更新的问题,但是还是没有解决元素多的时候,查询复杂度高的问题
4
曾轼麟
2021-08-08
回答老师的提问,ziplist 能支持的最大整数是多大? 分析步骤: 1、按照问题范围,首先我去查看了zipTryEncoding的实现(ziplist.c),其中在给encoding赋值的时候划分了:极小值,int8,int16,int24,int32和int64 2、此外发现当value大于int32最大值的时候,会统一使用ZIP_INT_64B去编码 3、但是在zipTryEncoding开头处发现了一个判断,entrylen >= 32的时候是不允许编码,意思就是传入的数据如果entrylen大于32直接跳过不编码为int 4、最后调用string2ll尝试将string value编码为long long(转换成功为1失败为0) 5、若转换成功,编码类型放到encoding中,值放到v中 根据第三步个人判断,传入的int值大小不会超过32位,那么最大值应该就是int32的最大值
共 2 条评论
3
Geek_0cfc2d
2022-02-23
老师,有个问题想请教一下: listpack 中如果不考虑逆序查询,entry 其实使用 encoding+data 就可以,那 entry 中最后一个 len 其实是为了逆序遍历而加入的,这样理解对吗?
1
胡玲玲
2021-08-07
请问ziplist、quicklist、listpack 这三者是如何协助redis的数据类型的呢
1
dawn
2022-08-29
来自江苏
如果数据存在连续内存里,针对插入和删除操作,只要不是最后一个节点,不都需要给后续的节点重新分配内存地址嘛,listpack并没有解决这个问题啊?而quicklist解决这个问题是方式,实际上是用了链表而非连续空间,牺牲了空间来解决这个事的,那还不如直接全上链表,根据类型归类也挺麻烦的
🤐
2022-07-24
作为一个写JAVA的,有点理解不了这里
孤独患者
2022-06-15
quicklist,如果更新某个节点数据,导致节点内存变大了,那是不是当前节点的后续节点都要往后移动呢?因为内存是连续的
共 1 条评论
柏油
2022-01-05
在连锁更新代码块中,只看到调用了一次ziplistResize进行内存重分配;在这之前会将所有连锁更新影响的entry找出来,并重新计算len,这样就可以一次性计算得到所有需要的内存大小,也就是只有一次ziplistResize内存重新分配。不过,可能会调用多次memmove来调整元素的位置。 文章中: “ziplist 新增某个元素或修改某个元素时,可能会导致后续元素的 prevlen 占用空间都发生变化,从而引起连锁更新问题,导致每个元素的空间都要重新分配” 每个元素都会进行内存重分配是不是有问题?还望解答
共 1 条评论