为什么需要 Listpack
- Ziplist 会出现 “连锁更新” 的现象,为了解决这个问题,Redis 引入了 QuickList,通过控制 QuicklistNode 结构里的压缩列表的大小或者元素个数,来减少连锁更新带来的性能影响。但是 QuickList 并没有完全解决连锁更新的问题。
- 为了彻底避免连锁更新的出现,Redis 在 Redis 7 引入了 Listpack ,代替 ZipList 彻底解决连锁更新的问题。
Listpack 是怎么避免连锁更新的?
- 我们知道,ZipList 会有连锁更新的问题,最根本的原因就是因为 prevlen 的存在,当 ZipList 新增某个元素或修改某个元素时,如果空间不不够,压缩列表占用的内存空间就需要重新分配。而当新插入的元素较大时,可能会导致后续元素的 prevlen 占用空间都发生变化,从而引起连锁更新问题。
- 既然这样,prelen 是留不得了!那么,Listpack 的结构长什么样子的?没有了 prelen 它又怎么实现遍历操作呢?
Listpack 的底层结构
Listpack
我们先来看看 listpack 整体长什么样子,这里看到 lpNew**(size_t capacity)
这个函数
c
复制代码
/* Create a new, empty listpack. * On success the new listpack is returned, otherwise an error is returned. * Pre-allocate at least `capacity` bytes of memory, * over-allocated memory can be shrunk by `lpShrinkToFit`. * */ unsigned char *lpNew(size_t capacity) { unsigned char *lp = lp_malloc(capacity > LP_HDR_SIZE+1 ? capacity : LP_HDR_SIZE+1); if (lp == NULL) return NULL; lpSetTotalBytes(lp,LP_HDR_SIZE+1); lpSetNumElements(lp,0); lp[LP_HDR_SIZE] = LP_EOF; return lp; }
可以看到,这个函数的主要功能是创建一个新的 listpack,其中各个成员变量的值如下:
-
lpSetTotalBytes,用于保存 listpack 的总字节