Redis 列表类型

530 阅读7分钟

列表类型(list)可以存储一个有序(推入顺序)的、可重复的字符串列表,列表类型内部是使用双向链表实现的,所以向列表两端添加元素的时间复杂度为 O(1),获取的元素越接近两端速度就越快。这种特性使列表类型非常适合日志、最新评论、新鲜事等业务场景,因为这些业务只关心最新的数据,取出最新数据的性能不受列表中总数据量的影响。一个列表类型键最多能容纳 232−1 个元素。

借助列表类型的一些命令,还可以实现一些高级功能,如任务队列、消息队列、阻塞队列、优先级队列等。

LPUSH

此命令可以将一个或多个元素推入到给定列表的左端,并返回操作后的列表数量,若指定的 key 不存在会先创建一个空列表,若 key 不是列表类型会返回错误。

LPUSH key value[value...]

例如:

redis> LPUSH list a b c
(integer) 3
redis> LRANGE list 0 -1
1) "c"
2) "b"
3) "a"

此命令的时间复杂度是 O(1)。

RPUSH

与 LPUSH 的区别是 RPUSH 是将元素推入到列表的右端。

LPOP

LPOP 命令会移除位于列表最左端的元素,并将被移除的元素返回。

LPOP key

例如:

redis> LPUSH list a b c
(integer) 3
redis> LPOP list
"c"

若 key 不存在或者列表为空将返回 nil,若 key 不是列表类型会返回错误,此命令的时间复杂度是 O(1)。

RPOP

与 LPOP 的区别是 RPOP 会弹出列表右端的第一个元素。

结合上面这4条命令,可以模拟栈和队列的操作:如果想把列表当作栈,则搭配使用 LPUSH 和 LPOP,或者搭配使用 RPUSH 和 RPOP;如果想把列表当作队列,则搭配使用 LPUSH 和 RPOP,或者搭配使用 RPUSH 和 LPOP。

LPUSHX & RPUSHX

与 LPUSH 和 RPUSH 不同的是,LPUSHX 和 RPUSHX 仅仅会在列表已经存在且不为空的情况下才会将元素推入列表的左侧或右侧,并且不支持同时推入多个元素,若操作成功则返回列表长度,否则返回 0。

RPOPLPUSH

RPOPLPUSH 命令会先从 source 列表类型键的右边弹出一个元素,然后将其加入 destination 列表类型键的左边,并返回这个元素的值,整个过程是原子的。

RPOPLPUSH source destination

此命令执行成功后会返回被弹出的元素,若 source 不存在此命令将返回 nil 表示执行失败,若 destination 不存在此命令会先创建一个空列表。

例如:

redis> LPUSH list a b c
(integer) 3
redis> RPOPLPUSH list list
"c"
redis> LRANGE list 0 -1
1) "b"
2) "a"
3) "c"

当把列表类型作为队列使用时,RPOPLPUSH 命令可以很直观地在多个队列中传递数据。当 source 和 destination 相同时,RPOPLPUSH 命令会不断地将队尾的元素移到队首,借助这个特性我们可以实现一个网站监控系统:使用一个队列存储需要监控的网址,然后监控程序不断地使用 RPOPLPUSH 命令循环取出一个网址来测试其可用性。这里使用 RPOPLPUSH 命令的好处在于,在程序执行过程中仍然可以不断地向网址列表中加入新网址,而且整个系统容易扩展,允许多个客户端同时处理队列。

LLEN

LLEN 命令可以返回列表中元素的数量,若 key 不存在则返回 0。

LLEN key

它类似于 MySQL 的 SELECT COUNT(*) FROM table;。但是 LLEN 的时间复杂度是 O(1),它不需要去计算,而是直接取出现有的值。

LINDEX

列表中的每个元素都有一个与之对应的正数索引(0~N-1)和负数索引(-1~-N)。

图片.png

LINDEX 命令可以通过索引获取列表中对应的元素。若 index 超出列表的范围将返回 nil。

LINDEX key index

此命令的时间复杂度是 O(n),n 是列表的长度,但是对两端第一个元素操作的时间复杂度是 O(1)。

LRANGE

LRANGE 命令可以获取给定索引范围上的多个元素。

LRANGE key start end

返回的元素包括 start 和 end 索引位置的元素,这点和 Java 等语言中的含头不含尾存在区别。若 start 和 end 有一个超出了范围,Redis 会对超出范围的索引进行修正,若两个都超出范围,此命令会返回一个 empty list。

如下所示,是一个获取列表中全部元素的方式,但是在列表中数据量大的情况下要谨慎使用。

redis> LRANGE list 0 -1

LSET

通过 LSET 命令,为列表的指定索引设置新元素。若指定的 index 超出了范围,将返回一个错误。

LSET key index value

此命令的时间复杂度是 O(n),n 是列表的长度,但是对两端第一个元素操作的时间复杂度是 O(1)。

LINSERT

通过 LINSERT 命令,可以将一个新元素插入列表某个指定元素的前面或者后面。

LINSERT key before|after pivot value

执行成功后将返回列表长度,若指定的元素不存在,将返回 -1 表示插入失败。

LTRIM

LTRIM 命令会移除列表中位于给定索引范围之外的所有元素,只保留给定范围之内的元素。

LTRIM key start end

例如:

redis> LRANGE list 0 -1
1) "c"
2) "b"
3) "a"
redis> LTRIM list 1 2
ok
redis> LRANGE list 0 -1
1) "b"
2) "a"

LREM

通过 LREM 命令移除列表中的指定元素。并返回被移除的元素数量作为命令的返回值。

LREM key count value

count 参数的值决定了 LREM 命令移除元素的方式:

  • count=0,移除列表中包含的所有指定元素。
  • count>0,从列表的左端开始向右进行检查,并移除 count 个指定元素。
  • count<0,从列表的右端开始向左进行检查,并移除 abs(count) 个指定元素。

BLPOP

BLPOP 命令是带有阻塞功能的左端弹出操作,它接受多个列表以及一个秒级精度的超时时限作为参数。

BLPOP key[key...] timeout

当列表是空时,客户端将被阻塞,直到另一个客户端将元素推入到列表中,或者到达了指定的阻塞时间。若达到阻塞时间时列表仍然是空,此命令将返回 nil。

若同时存在多个客户端因为同一个列表被阻塞,Redis 会按照先阻塞先服务的原则,依次给各个客户端弹出列表元素。

若此命令弹出了一个元素,会返回两个值,第一个是列表名称,第二个是元素,这样做是为了在客户端监听多个列表被阻塞时知道弹出的元素属于哪个列表。借助这个特点可以实现一个优先级队列,当 BLPOP 监听多个列表时,若每个列表都有元素要弹出,Redis 会按照监听的列表顺序弹出各个列表中的元素,把优先级高的列表放在前面可以保证优先弹出这个列表中的元素。

BLPOP 命令的阻塞效果只对执行这个命令的客户端有效,其他客户端以及 Redis 服务器本身并不会因为这个命令而被阻塞。

BRPOP

类似于 BLPOP,支持带有阻塞效果的右端弹出。

BRPOPLPUSH

BRPOPLPUSH 命令是 RPOPLPUSH 命令的阻塞版本。

BRPOPLPUSH source destination timeout

根据源列表是否为空,BRPOPLPUSH 命令会产生以下两种行为:

  • 如果源列表非空,那么 BRPOPLPUSH 命令的行为就和 RPOPLPUSH 命令的行为一样。
  • 如果源列表为空,那么 BRPOPLPUSH 命令将阻塞执行该命令的客户端,然后在给定的时限内等待可弹出的元素出现,或者等待时间超过给定时限为止。

如果源列表在给定的时限内一直没有元素可供弹出,那么 BRPOPLPUSH 命令将向客户端返回一个 nil。