压缩列表(ziplist)是由一系列特殊编码的内存块构成的列表,它对于Redis的数据存储优化有着非常重要的作用。
为了更好的理解为什么压缩列表更为高效,我们需要从链表谈起。对于一个典型的双向链表,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。另外还有一个指针指向该节点的字符串。每一个字符串又实际分为三个部分:一个代表该字符串长度的整数,一个代表剩余字节的整数以及以“\0”结尾的字符串本身。以下是一个示例:
忽略其余细节,除字符串本身和空余的字节外,三个指针和两个整数都会占用额外的空间。而压缩列表转为存储上一个结点长度、当前结点长度以及字符串本身。如果存储指向上一个链表结点和指向下一个链表结点的指针需要8个字节,通过这样的“压缩”后在最好的情况下只需2个字节。也就是说,压缩列表大大节省了链表指针的存储。
那接下来,我们看一下如何确保正在使用压缩列表。
Redis有两个配置针对压缩链表:
list-max-ziplist-entries 512 list-max-ziplist-value 64
list-max-ziplist-entries指定最大的元素个数,而list-max-ziplist-value指定字符串的最大长度。
也就是说,当LIST的元素个数小于配置值list-max-ziplist-entries且元素值字符串的长度小于配置值list-max-ziplist-value,则该LIST可以采用压缩列表数据结构存储数据。
在实际操作中,对于Redis2.6而言,可以利用debug_object命令来检测一个LIST对象从而确定它当前是否采用ziplist。
>>> import redis >>> conn = redis.Redis() >>> conn.rpush('test', 'a', 'b', 'c', 'd') 4L >>> conn.debug_object('test') {'encoding': 'ziplist', 'refcount': 1, 'lru_seconds_idle': 10, 'lru': 317696, 'at': '004ea964', 'serializedlength': 24, 'type': 'Value'} >>> conn.rpush('test', 65*'a') 5L >>> conn.debug_object('test') {'encoding': 'linkedlist', 'refcount': 1, 'lru_seconds_idle': 10, 'lru': 317697, 'at': '004ea964', 'serializedlength': 22, 'type': 'Value'} >>> conn.rpop('test') 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' >>> conn.debug_object('test') {'encoding': 'linkedlist', 'refcount': 1, 'lru_seconds_idle': 0, 'lru': 317699, 'at': '004ea964', 'serializedlength': 9, 'type': 'Value'}
从以上示例可以验证刚才所说的,并且可以发现,当一个LIST由ziplist转为linkedlist后,即使之后它再次满足了采用压缩列表数据结构存储数据的条件,也不会再转回来。
一些参考资料值得去更多的研究一下:
GREAT!
Great!