redis源码之list结构的实现
关于redis的list的常用命令就不多说了 常用的命令lpush,rpush,lpop,rpop,lrangge等,这个不错过多的演示,相信研究源码的同学应该都知道这些,唯一要说的就是在redis中有BRPOP等命令,这个是通过redisDb中的blocking_keys和ready_keys来实现,当然这个不是咱们这篇文章需要讨论的重点
typedef struct redisDb {
dict *dict; /* The keyspace for this DB 字典数据结构,非常重要*/
dict *expires; /* Timeout of keys with a timeout set 过期时间*/
dict *blocking_keys; /* Keys with clients waiting for data (BLPOP) list一些数据结构中用到的阻塞api*/
dict *ready_keys; /* Blocked keys that received a PUSH */
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS 事务相关处理 */
int id; /* Database ID */
long long avg_ttl; /* Average TTL, just for stats */
unsigned long expires_cursor;/* Cursor of the active expire cycle. */
list *defrag_later; /* List of key names to attempt to defrag one by one, gradually. */
} redisDb;
对于redis的list来说,list是一个有序(按照加入的时序来排序)的数据结构,redis采用quicklist(双端链表)和ziplist作为list的底层实现 我们先来看看ziplist的结构图
具体各个属性的含义都在图片中有所说明,其实ziplist就是一个非常紧凑的数据结构,非常节省空间,但是它也存在对应的问题。比如我们在新加一个元素的时候,我们需要重新分配一块内存空间,然后再把数据全部拷贝过来,如果数据量小还好说,数据量大了非常影响性能,所以redis中在处理list的时候引入了quicklist的数据结构其实quicklist的实现也比较简单,就是用了一个双向链表把各个ziplist给连接了起来。那么问题来了,每个quicklistNode中的ziplist是多大呢?根据什么来拆分呢?在redis.conf中
# Lists are also encoded in a special way to save a lot of space.
# The number of entries allowed per internal list node can be specified
# For a fixed maximum size, use -5 through -1, meaning:
# -5: max size: 64 Kb <-- not recommended for normal workloads
# -4: max size: 32 Kb <-- not recommended
# -3: max size: 16 Kb <-- probably not recommended
# -2: max size: 8 Kb <-- good
# -1: max size: 4 Kb <-- good
# Positive numbers mean store up to _exactly_ that number of elements
# The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size),
list-max-ziplist-size -2 //单个ziplist节点最多存储8kb,超过则进行分裂,将数据存储在新的ziplist节点中
双向链表的问题就是会造成很多不连续的内存碎片,于是就需要尽量减少list的片段,就需要进行压缩,在redis.conf中
# Lists may also be compressed.
# Compress depth is the number of quicklist ziplist nodes from *each* side of
# 0: disable all list compression
# 1: depth 1 means "don't start compressing until after 1 node into the list,
# going from either the head or tail"
# So: [head]->node->node->...->node->[tail]
# [head], [tail] will always be uncompressed; inner nodes will compress.
# 2: [head]->[next]->node->node->...->node->[prev]->[tail]
# 2 here means: don't compress head or head->next or tail->prev or tail,
# 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail]
list-compress-depth 0//0代表所有节点,都不进行压缩,1代表从头节点往后走一个,尾节点往前走一个不用压缩,2、3、4以此类推 这个目的主要是减少内存的碎片