牺牲速度来节省内存,Redis是觉得自己太快了吗 (3)

第五组 2 个字节 [02 f6] 就是第二个 entry,02 即为 2,表示前一个节点的长度为 2,注意,因为这里算出来的结果是小于 254,所以就代表了这里只用到了 1 个字节来存储上一个节点的长度(如果等于 254,这说明接下来 4 个字节才存储的是长度),所以后面的 f6 就是当前节点的数据,转换成二进制为 11110110,对应了表格中的编码 1111xxxx,同样的后四位 0110 存储的是真实数据,计算之后得出是5。

最后一组1个字节[ff]转成二进制就是 11111111,代表这是整个 ziplist 的结尾。

假如这时候又添加了一个 Hello World 字符串到列表中,那么就会新增一个 entry,如下所示:

[02] [0b] [48 65 6c 6c 6f 20 57 6f 72 6c 64]

第一组的 1 个字节 02 转成十进制就是 2 ,表示前一个节点(即上面示例中的 [02 f6])长度是 2。

第 二组的2 个字节 0b 转成二进制为 00001011,以 00 开头,符合编码 00pppppp,而除掉最开始的两位 00,计算之后得到十进制 11,这就说明后面字节数组的长度是 11。

第三组刚好是 11 个字节,对应了上面的长度,所以这里就是真正存储了 Hello World 的字节数组。

ziplist 连锁更新问题

上面提到 entry 中的 prevlen 属性可能是 1 个字节也可能是 5 个字节,那么我们来设想这么一种场景:假设一个 ziplist 中,连续多个 entry 的长度都是一个接近但是又不到 254 的值(介于 250~253 之间),那么这时候 ziplist 中每个节点都只用了 1 个字节来存储上一个节点的长度,假如这时候添加了一个新节点,如 entry1 ,其长度大于 254 个字节,此时 entry1 的下一个节点 entry2 的 prelen 属性就必须要由 1 个字节变为 5 个字节,也就是需要执行空间重分配,而此时 entry2 因为增加了 4 个字节,导致长度又大于 254 个字节了,那么它的下一个节点 entry3 的 prelen 属性也会被改变为 5 个字节。依此类推,这种产生连续多次空间重分配的现象就称之为连锁更新。同样的,不仅仅是新增节点,执行删除节点操作同样可能会发生连锁更新现象。

虽然 ziplist 可能会出现这种连锁更新的场景,但是一般如果只是发生在少数几个节点之间,那么并不会严重影响性能,而且这种场景发生的概率也比较低,所以实际使用时不用过于担心。

总结

本文主要讲解了 Redis 当中的 ziplist(压缩列表),一种用时间换取空间的数据结构,在介绍压缩列表存储结构的同时通过一个存储示例来分析了 ziplist 是如何存储数据的,最后介绍了 ziplist 中可能发生的连锁更新问题。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpyzwx.html