这是我参与8月更文挑战的第19天,活动详情查看:8月更文挑战
面试加持文章,极速面试点。主要是说几个数据结构的作用已经在 redis 的组合:
- ziplist
- quicklist
- listpack
- skiplist
ziplist
设计初衷:节省内存。在设计数据结构协议,将内存利用率发挥到机制:
-
每一个 entry 整体存储分为:
- pre_entry_lenth
- encoding + len → 会根据第 1 个字节分析存储类型
- content → 16 进制
-
整体分为:
- header ⇒ [zlbytes, zltail, zllen]
- entrys → 实际存储
- tailer → 标记结束
-
从 encoding 就知道,内存分配会根据类型选择最小分配
劣势也很明显:
- 每一个元素存储上一个元素的 length (便于反向遍历)。在修改过程中,如果导致长度发生变化,会发生前面元素的连锁变化 → 连锁更新导致频繁申请内存
- 不管是反向遍历 (好歹可以反向),还是正向 ⇒ 都需要挨个遍历,在 entry 过多的情况,这是一个很耗费性能的点
为了解决这些问题,再包装一层就行 → qucklist
qucklist
简单来说:ziplist1 <-> ziplist2 <-> ziplist3 <-> ziplist4。而每一个 ziplist 其实是 quicklistNode
List 底层的数据结构就是 qucklist。因为它是一个链表,所以 LPUSH/LPOP/RPUSH/RPOP 的复杂度是 O(1) ,可以快速删除加入
优化的点:
-
list-max-ziplist-size
限制每一个 node 可以存储的 ziplist 的数量或者是个数- 正数 → 最多包含多少个数据项
- 负数 → 每个 ziplist 存储最大的字节数。如:-2 ⇒ 8kb
-
超过每一个 node 限制,会新建一个 node,然后把 ziplist 存进去
-
List 更多的操作集中在 链头和链尾,所以可以将中间元素进行压缩,进行存储空间优化。默认不压缩 ⇒ l
ist-compress-depth
但是没有解决 ziplist 级联更新的问题,所以出现了:listpack
listpack
本质要解决级联更新的问题,其实在存储结构 ⇒ 不要让每个元素保存上一个元素的 length 即可
存储优化:
- 每个元素项不再保存上一个元素的长度,而是优化元素内字段的顺序,来保证既可以从前也可以向后遍历
- 但是由于 List/Hash/Set/ZSet 都严重依赖 ziplist ,目前只有 stream 使用了 listpack