Redis数据结构和内部编码--列表(list)

 Redis数据结构和内部编码--列表(list)

一.概念

列表用来存储多个有序的字符串,列表中的每个字符串成为元素,一个列表最多可以存储2^32-1个元素。列表是一种比较灵活的数据结构,它可以充当栈和队列的角色。

列表类型有两个特点:

1.      列表中的元素是有序的,这就意味着可以通过下表获取某个元素或者某个范围内的元素。

2.      列表中的元素可以是重复的

二. 命令
2.1 从右边插入元素

命令:rpush key value1 value2..

例如:rpush list v1 v2

返回:2,代码列表当前的长度。

     lrange list 0 -1

     返回:v1

           V2

2.2 从左边插入元素

命令:lpush key value1 value2..

例如:lpush list v1 v2

     lrange list 0 -1

     返回:v2

           V1

2.3 向某个元素前或者后插入元素

命令: linsert key before|after select value

例如: lpush list v1 v2 v3 v4 v5

linsert list before v3 insert1

返回:6,返回结果代表当前命令的长度

      lrange list 0 -1

返回:

1) "v5"

2) "v4"

3) "insert1"

4) "v3"

5) "v2"

6) "v1"

2.4 获取指定范围内的元素列表

命令:lrange key start end

     start和end代表索引下标,从左向右是0到N-1,从右向左是-1到-N

例如:lpush list v1 v2

     lrange list 0 -1

返回:v2

     V1

 

2.5 获取列表指定索引下标的元素

命令:lindex key index

例如:lpush list v1 v2 v3 v4 v5

     Lindex list 3

返回:v3

2.6 获取列表长度

命令:llen key

例如:lpush list v1 v2 v3 v4 v5

     llen list

返回:5

2.7 从列表左侧弹出元素

命令:lpop key

例如:lpush list v1 v2 v3 v4 v5

     rpop list

返回:v1,返回被删除的元素

再次查看list中的元素,lrange list 0 -1

返回:v2

v3

  v4

  v5

2.8 从列表右侧弹出元素

命令:rpop key

例如:lpush list v1 v2 v3 v4 v5

     rpop list

返回:v5,返回被删除的元素

再次查看list中的元素,lrange list 0 -1

返回:v1

v2

  v3

  v4

2.9 删除指定元素

命令:lerm key count value 从列表中找到等于value的元素进行删除,根据count的不同分三种情况:

(1)      count>0,从左向右删除最多count个值等于value的元素

(2)      count<0,从右向左删除最多count绝对值个值等于value的元素

(3)      count=0,删除所有

例如:lpush list v1 v2 v3 v2 v5

     lrem list 0 v2

返回:2

再次查看list中的元素,lrange list 0 -1

返回:v1

v3

  v5

 

2.10 按照索引范围修剪列表

命令:ltrim key start end

例如:lpush list v1 v2 v3 v4 v5

ltrim list 1 3

返回:ok

再次查看list中的元素,lrange list 0 -1

返回:v2

     v3

     v4

2.11 修改指定索引下标的元素

命令:lset key index value

例如:lpush list v1 v2 v3 v4 v5

     lset list 3 v8

返回:ok

再次查看list中的元素,lrange list 0 -1

返回:v1

     v2

     v3

     v8

     v5

2.12 阻塞式弹出

(1)命令:blpop key[key1 …] timeout 从列表左侧弹出元素,阻塞时间为timeout秒,超过timeout秒则客户端返回,如果timeout为0,则始终阻塞。

(2)命令:brpop key[key1 …] timeout 从列表右侧弹出元素,阻塞时间为timeout秒,超过timeout秒则客户端返回,如果timeout为0,则始终阻塞。

 

例如:blpop list 3

返回:此时由于列表list中没有任何元素,客户端阻塞3秒,如果三秒钟内有其他客户端向list列表中加入元素,则此客户端立即返回。否则阻塞3秒钟,然后返回nil

注:如果多个客户端执行blpop list,最先执行命令的客户端率先返回数值。

 

三.list类型命令的时间复杂度

Redis数据结构和内部编码--列表(list)

四.内部编码

hash类型的内部编码有两种:

(1)      ziplist(压缩列表)

当列表的元素个数小于list-max-ziplist-entries配置(默认512个),同时所有值都小于list-maxziplist-value配置(默认为64字节),Redis会使用ziplist做为哈希的内部实现。Ziplist可以使用更加紧凑的结构来实现多个元素的连续存储,所以在节省内存方面更加优秀。

压缩列表的优点:

压缩列表ziplist结构本身就是一个连续的内存块,由表头、若干个entry节点和压缩列表尾部标识符zlend组成,通过一系列编码规则,提高内存的利用率,使用于存储整数和短字符串。

压缩列表的缺点:

 

每次插入或删除一个元素时,都需要进行频繁的调用realloc()函数进行内存的扩展或减小,然后进行数据”搬移”,甚至可能引发连锁更新,造成严重效率的损失。

 

(2)      linkedlist(链表)

当列表类型无法满足ziplist要求时,redis会采用linkedlist做为列表的内部实现。

 

(3)quicklist(快速列表)

quicklist结构是在Redis 3.2版本中新加的数据结构,用在列表的底层实现

quicklist宏观上是一个双向链表,因此,它具有一个双向链表的有点,进行插入或删除操作时非常方便,虽然复杂度为O(n),但是不需要内存的复制,提高了效率,而且访问两端元素复杂度为O(1)。

quicklist微观上是一片片entry节点,每一片entry节点内存连续且顺序存储,可以通过二分查找以 log2(n) 的复杂度进行定位。

quicklist的结构为什么这样设计呢?总结起来,大概又是一个空间和时间的折中:

· 双向链表便于在表的两端进行push和pop操作,但是它的内存开销比较大。首先,它在每个节点上除了要保存数据之外,还要额外保存两个指针;其次,双向链表的各个节点是单独的内存块,地址不连续,节点多了容易产生内存碎片。

· ziplist由于是一整块连续内存,所以存储效率很高。但是,它不利于修改操作,每次数据变动都会引发一次内存的realloc。特别是当ziplist长度很长的时候,一次realloc可能会导致大批量的数据拷贝,进一步降低性能。

 

在quicklist表头结构中,有两个成员是fill和compress,其中” : “是位域运算符,表示fill占int类型32位中的16位,compress也占16位。

fill和compress的配置文件是redis.conf。

  • fill成员对应的配置:list-max-ziplist-size -2 

    • 当数字为负数,表示以下含义:
    • -1 每个quicklistNode节点的ziplist字节大小不能超过4kb。(建议)
    • -2 每个quicklistNode节点的ziplist字节大小不能超过8kb。(默认配置)
    • -3 每个quicklistNode节点的ziplist字节大小不能超过16kb。(一般不建议)
    • -4 每个quicklistNode节点的ziplist字节大小不能超过32kb。(不建议)
    • -5 每个quicklistNode节点的ziplist字节大小不能超过64kb。(正常工作量不建议)
    • 当数字为正数,表示:ziplist结构所最多包含的entry个数。最大值为 215。
  • compress成员对应的配置:list-compress-depth 0 

    • 后面的数字有以下含义:
    • 0 表示不压缩。(默认)
    • 1 表示quicklist列表的两端各有1个节点不压缩,中间的节点压缩。
    • 2 表示quicklist列表的两端各有2个节点不压缩,中间的节点压缩。
    • 3 表示quicklist列表的两端各有3个节点不压缩,中间的节点压缩。
    • 以此类推,最大为 216。
五. 列表的使用场景及方案

(1)lpush+lpop=栈

(2)lpush+rpop=队列

(3)lpush+ltrim=有限集合

(4)lpush+brpop=消息队列