前言

上篇文章中,我们介绍了Redis的部分命令,本篇文章我们继续学习剩余命令,如果对上次命令淡忘的小伙伴可以先复习下哟。

LLEN

可用版本:>= 1.0.0

时间复杂度: O(1)

命令格式

1
LLEN key

命令描述

  • 返回列表长度
  • 如果列表不存在,返回0
  • 如果key对应的value类型不是列表,返回error

返回值

整数:列表长度

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
127.0.0.1:6379> lpush mylist world
(integer) 1
127.0.0.1:6379> lpush mylist hello
(integer) 2
127.0.0.1:6379> llen mylist
(integer) 2

# 不存在的key
127.0.0.1:6379> llen notexistkey
(integer) 0

# value类型不是列表,llen报错
127.0.0.1:6379> set key1 value1
OK
127.0.0.1:6379> llen key1
(error) WRONGTYPE Operation against a key holding the wrong kind of value

LREM

可用版本:>= 1.0.0

时间复杂度: O(N+M),N为列表长度,M为移除元素个数

命令格式

1
LREM key count element

命令描述

  • 根据给定的参数 element ,移除列表中前 count 个与 element 相等的元素
  • count>0: 从表头至表尾顺序移除
  • count<0: 从表尾至表头顺序移除
  • count=0: 移除列表中所有与element相等的元素
  • LREM list -2 "hello"相当于移除 list 中最后的两个 hello

返回值

整数:被移除的元素个数(key不存在时,返回0)

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
127.0.0.1:6379> rpush llenlist hello
(integer) 1
127.0.0.1:6379> rpush llenlist hello
(integer) 2
127.0.0.1:6379> rpush llenlist world
(integer) 3
127.0.0.1:6379> rpush llenlist hello
(integer) 4
127.0.0.1:6379> rpush llenlist hello
(integer) 5
127.0.0.1:6379> lrange llenlist 0 -1
1) "hello"
2) "hello"
3) "world"
4) "hello"
5) "hello"

# count>0,从表头开始移除
127.0.0.1:6379> lrem llenlist 1 hello
(integer) 1
127.0.0.1:6379> lrange llenlist 0 -1
1) "hello"
2) "world"
3) "hello"
4) "hello"

# count<0,从表尾开始移除
127.0.0.1:6379> lrem llenlist -1 hello
(integer) 1
127.0.0.1:6379> lrange llenlist 0 -1
1) "hello"
2) "world"
3) "hello"

# count=0,移除所有指定元素
127.0.0.1:6379> lrem llenlist 0 hello
(integer) 2
127.0.0.1:6379> lrange llenlist 0 -1
1) "world"

LINSERT

可用版本:>= 2.2.0

时间复杂度:O(N),N为遍历到指定元素pivot需访问的元素个数,pivot为表头则为O(1),表尾则为O(N)

命令格式

1
LINSERT key BEFORE|AFTER pivot element

命令描述

  • 将一个元素值 element ,插入到列表中指定元素 pivot 的之前或之后
  • key不存在时,不执行任何操作
  • 如果key对应的value类型不是列表,返回error

返回值

整数:插入元素后列表长度(如果未找到 pivot,则返回 -1 )

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
127.0.0.1:6379> rpush insertlist hello
(integer) 1
127.0.0.1:6379> rpush insertlist world
(integer) 2
127.0.0.1:6379> lrange insertlist 0 -1
1) "hello"
2) "world"

# before插入
127.0.0.1:6379> linsert insertlist before hello my
(integer) 3
127.0.0.1:6379> lrange insertlist 0 -1
1) "my"
2) "hello"
3) "world"

# after插入
127.0.0.1:6379> linsert insertlist after world end
(integer) 4
127.0.0.1:6379> lrange insertlist 0 -1
1) "my"
2) "hello"
3) "world"
4) "end"

# 指定pivot不存在
127.0.0.1:6379> linsert insertlist after name lifelmy
(integer) -1

LSET

可用版本:>= 1.0.0

时间复杂度:O(N),N为列表长度,如果设置的索引为表头或表尾,则复杂度为O(1)

命令格式

1
LSET key index element

命令描述

  • 将索引为 index 的值设置为 element
  • 索引超出列表范围,返回error

返回值

字符串:“OK”

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
127.0.0.1:6379> lpush mylist world
(integer) 1
127.0.0.1:6379> lpush mylist hello
(integer) 2
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "world"

# 设置索引0
127.0.0.1:6379> lset mylist 0 west
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "west"
2) "world"

# 超出列表范围
127.0.0.1:6379> lset mylist 3 test
(error) ERR index out of range

LPOS

可用版本:>= 6.0.6

时间复杂度:O(N),N为指定元素在列表中的个数。

命令格式

1
LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len]

命令描述

  • 返回指定元素 element 在列表中的索引
  • 没有指定可选参数时,默认从表头搜索至表尾,返回第一个匹配的索引;如果没有匹配到,返回 nil
1
2
3
> RPUSH mylist a b c 1 2 3 c c
> LPOS mylist c
2
  • 可选参数 rank :当列表中有多个匹配时,返回第 rank 个匹配的索引。当 rank 为正数时,从表头搜索至表尾返回索引;当 rank 为负数时,从表尾搜索至表头
  • 无论rank 是正数或者负数,返回的索引始终是正数,即以0开始的索引值
1
2
3
4
5
6
7
# 返回第二个匹配的索引
> LPOS mylist c RANK 2
6

# 从表尾开始搜索,返回第一个匹配的索引
> LPOS mylist c RANK -1
7
  • 可选参数 count : 返回多个匹配到的索引;count=0 表示返回所有匹配索引
1
2
3
4
5
> LPOS mylist c COUNT 2
[2,6]

> LPOS mylist c COUNT 0
[2,6,7]
  • rank + count : 从第 rank 个匹配到的索引开始,返回 count 个索引
1
2
> LPOS mylist c RANK -1 COUNT 2
[7,6]
  • 当没有匹配到值时,如果提供了可选参数 count ,返回空数组;否则返回 nil
  • 可选参数 MAXLEN :至多访问列表中的 MAXLEN 个元素进行比较,0 表示遍历所有元素比较

返回值

没有可选参数:返回元素索引 或者 nil (元素不存在)

提供count参数: 返回数组

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
127.0.0.1:6379> rpush mykey a b c d a b c a
(integer) 8

# 默认从表头开始
127.0.0.1:6379> lpos mykey a
(integer) 0

# rank参数,从表头开始搜索,返回第二个匹配到的索引
127.0.0.1:6379> lpos mykey a rank 2
(integer) 4

# rank参数,从表尾开始搜索,返回第二个匹配的索引
127.0.0.1:6379> lpos mykey a rank -2
(integer) 4

# 指定count参数,返回多个匹配索引
127.0.0.1:6379> lpos mykey a count 3
1) (integer) 0
2) (integer) 4
3) (integer) 7

# rank+count, 从第二个匹配开始返回3个索引值(没有更多匹配了,只返回2个)
127.0.0.1:6379> lpos mykey a rank 2 count 3
1) (integer) 4
2) (integer) 7

LTRIM

可用版本:>= 1.0.0

时间复杂度:O(N),N为被移除的元素个数

命令格式

1
LTRIM key start stop

命令描述

  • 对一个列表就行截取,保留给定范围内的元素,其余元素会被删除
  • startstop 可以是正数或者负数:正数表示从表头开始(索引为0),负数表示从表尾开始(索引为-1)
  • 给定参数超出列表范围不会报错:
    • start 超出列表范围或者 start > stop,结果是空列表
    • stop 超出列表范围,处理完元素就结束

返回值

字符串:“OK”

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
127.0.0.1:6379> rpush mylist a b c d e
(integer) 5
127.0.0.1:6379> ltrim mylist 1 3
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "b"
2) "c"
3) "d"

127.0.0.1:6379> rpush anotherlist a b c d e
(integer) 5
127.0.0.1:6379> ltrim anotherlist 2 -2
OK
127.0.0.1:6379> lrange anotherlist 0 -1
1) "c"
2) "d"

# start 超出范围
127.0.0.1:6379> ltrim anotherlist 3 -1
OK
127.0.0.1:6379> lrange anotherlist 0 -1
(empty array)

BLPOP

可用版本:>= 2.0.0

时间复杂度:O(N),N为命令提供key的个数

6.0版本后,timeout参数为double浮点类型,而非integer整形

命令格式

1
BLPOP key [key ...] timeout

命令描述

  • BLPOP 是列表的阻塞式弹出原语
  • BLPOPLPOP 命令的阻塞版本,当给定的列表中没有元素可以弹出时,会一直阻塞住,直到列表中有元素可以弹出或者阻塞时间超时。当指定了多个列表时,会按照顺序弹出第一个非空的列表元素
  1. 非阻塞行为
  • 当调用BPOP时,会按照顺序检查列表是否非空,返回第一个非空列表名称和其表头元素值。
  • 当调用 BLPOP list1 list2 list3 0 时, 如果list1为空,list2 list3非空,那么会返回list2以及其表头元素
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
127.0.0.1:6379> rpush list1 hello
(integer) 1
127.0.0.1:6379> rpush list4 world
(integer) 1
127.0.0.1:6379> blpop list1 list2 list3 list4 0
1) "list1"
2) "hello"


127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> rpush list1 hello
(integer) 1
127.0.0.1:6379> rpush list4 world
(integer) 1
127.0.0.1:6379> blpop list2 list3 list4 list1 0
1) "list4"
2) "world"
  1. 阻塞行为
  • 当给定的key都不存在时,即列表不存在,BLPOP 会一直阻塞,直到有其他客户端执行了 LPUSHRPUSH 命令创建了列表
  • 当列表中有值后,BLPOP 就会返回列表名及弹出的元素值
  • timeout 指定阻塞时间,0 表示阻塞时间可以无限期延长,当指定非零参数时,阻塞时间到且列表中还无数据,返回nil
1
2
3
4
# 列表不存在,指定了timeout=10, 10s后返回nil
127.0.0.1:6379> blpop notexistslist 10
(nil)
(10.06s)
1
2
3
4
5
6
7
8
9
# 客户端1调用不存在的key,会一直阻塞 
127.0.0.1:6379> blpop blocklist 0
1) "blocklist"
2) "name"
(48.18s)

# 另起一个客户端2,使用rpush插入数据,第一个客户端就会返回
127.0.0.1:6379> rpush blocklist name
(integer) 1
  1. 优先级问题

BLPOP命令指定了多个列表,会按照从左到右的顺序,返回第一个非空列表中的元素。假设执行 BLPOP key1 key2 key3 key4 0, key2key4 非空,那么会返回 key2 中的元素

多个客户端阻塞在同一个key上: 按照先来先服务的原则,返回给当前所有客户端中到达最早的一个客户端

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 客户端1
client1 > blpop priorlist 0

# 客户端2
clietn2 > blpop priorlist 0

# 客户端3
client3 > lpush priorlist lifelmy
(integer) 1

# 客户端1
client1 > blpop priorlist 0
1) "priorlist"
2) "lifelmy"
(83.44s)
  1. 多个元素push到列表问题

当执行 MULTI EXEC 事务一次插入多个元素,或者使用类似 LPUSH mylist a b c 的命令一次插入多个元素时,BLPOP 在不同版本中返回的数据是不一样的。

假如客户端A执行 BLPOP 被阻塞, 客户端B 执行 LPUSH 命令:

1
2
Client A:   BLPOP foo 0
Client B:   LPUSH foo a b c

Redis 2.4版本 : 返回 a:当客户端B开始插入时,客户端A发现数据就会返回。因为第一个插入的是 a,所以客户端A就返回 a

Redis 2.6及更高版本: 返回 c:当客户端B将命令执行完后,数据才会对阻塞的客户端A可见。客户端B执行 LPUSH 后,c在表头,因此返回 c

1
2
3
4
5
6
7
8
# 6.2.4版本
cliet1 > blpop foo 0
1) "foo"
2) "3"
(26.31s)

client2 > lpush foo 1 2 3
(integer) 3
  1. 在MULTI/EXEC事务中使用BLPOP

MULTI、EXEC事务中执行BLPOP没有意义,因为事务会阻塞其他命令执行 LPUSHRPUSH等命令,这样事务就一直阻塞在BLPOP无法返回,形成死锁。因此在事务中执行BLPOP,和执行LPOP的效果一样,不阻塞直接返回

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 1. 执行事务时,list中有数据直接返回
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> lpush mylist 1
(integer) 1
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> blpop mylist 0
QUEUED
127.0.0.1:6379(TX)> exec
1) 1) "mylist"
   2) "1"

# 2. 列表不存在,事务中的 BLPOP 直接返回 nil
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> blpop otherlist 0
QUEUED
127.0.0.1:6379(TX)> exec
1) (nil)

返回值

nil: 列表无数据且超时

数组:获取到数据,数组第一个元素为列表名,第二个为元素值

使用姿势:事件提醒

有时候,为了等待一个新元素到达数据中,需要使用轮询的方式对数据进行探查。

另一种更好的方式是,使用系统提供的阻塞原语,在新元素到达时立即进行处理,而新元素还没到达时,就一直阻塞住,避免轮询占用资源。BLPOP就可以解决这个问题。

比如我们要处理其他类型的数据,比如集合类型(Set)数据,而set没有阻塞原语,我们可以结合set和list一起使用

消费者: 使用SPOPset中弹出元素,如果没有元素就阻塞到BLPOP命令。当有数据到来时,BLPOP停止阻塞, SPOP开始遍历set

1
2
3
4
5
6
LOOP forever
    WHILE SPOP(key) returns elements
        ... process elements ...
    END
    BLPOP helper_key
END

生产者:生产数据时,同时添加到setlist

1
2
3
4
MULTI
SADD key element
LPUSH helper_key x
EXEC

BRPOP

可用版本:>= 2.0.0

时间复杂度:O(N),N为命令提供key的个数

6.0版本后,timeout参数为double浮点类型,而非integer整形

命令格式

1
BRPOP key [key ...] timeout

命令描述

  • BRPOP 是列表的阻塞式弹出原语
  • BLPOPRPOP 命令的阻塞版本,当没有元素可以弹出时会一直阻塞;列表非空时从队尾弹出。
  • 具体命令描述见 BLPOP

返回值

nil: 列表无数据且超时

数组:获取到数据,数组第一个元素为列表名,第二个为元素值

示例

1
2
3
4
5
6
7
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> rpush list a b c
(integer) 3
127.0.0.1:6379> brpop list 0
1) "list"
2) "c"

BRPOPLPUSH

可用版本:>= 2.2.0

时间复杂度: O(1)

自6.2.0,该命令已废弃,建议使用 BLMOVE,见下个命令

命令格式

1
BRPOPLPUSH source destination timeout

命令描述

  • BRPOPLPUSHRPOPLPUSH 的阻塞版本
  • source 中无数据时,会一直阻塞;当有数据时,将 source 表尾数据插入至 destination 表头

返回值

nil: 操作超时

字符串:弹出的元素值

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
127.0.0.1:6379> flushdb
OK

# 客户端1 阻塞
client1 > brpoplpush sourcelist destlist 0
"a"
(13.87s)

# 客户端2 push数据后客户端1 返回
127.0.0.1:6379> lpush sourcelist a
(integer) 1

使用姿势

  1. 安全队列

当我们从列表1中弹出数据进行处理时,列表1会删除该数据,此时数据只在我们的客户端中存在,如果此时客户端突然宕机了,那么这条数据就会丢失。为了防止数据丢失,我们可以使用RPOPLPUSH 或者 BRPOPLPUSH,在弹出元素的同时将元素加入到另一个列表中用于备份,客户端处理完本条后将备份列表中的元素删除。

  1. 循环队列

通过使用相同的 key 作为 RPOPLPUSHBRPOPLPUSH 命令的两个参数,客户端可以一个接一个地获取列表元,最终取得列表的所有元素,而不必像 LRANGE 命令那样一下子将所有列表元素都从服务器传送到客户端中。

以上的模式甚至在以下的两个情况下也能正常工作:

  • 多个客户端同时对同一个列表进行旋转,它们获取不同的元素,直到所有元素都被读取完,之后又从头开始。
  • 有其他客户端向列表尾部添加新元素。

比如我们要实现一个监控报警系统,多个服务器尽可能快速的检查多个网站是否正常,可以将多个网站存入列表中,多个服务器同时对该列表进行旋转处理。

BLMOVE

可用版本:>= 6.2.0

时间复杂度: O(1)

自6.2.0,该命令已废弃,建议使用 BLMOVE,见下个命令

命令格式

1
BLMOVE source destination LEFT|RIGHT LEFT|RIGHT timeout

命令描述

  • BLMOVELMOVE 的阻塞版本
  • source 中无数据时,会一直阻塞,其余操作和 LMOVE 一致

返回值

nil: 操作超时

字符串:弹出的元素值

示例

1
2
3
4
5
6
7
8
# 客户端1阻塞
client 1> blmove sourcelist destlist right left 0
"hello"
(14.21s)

# 客户端2插入数据后,客户端1返回数据
client 2> lpush sourcelist hello
(integer) 1

使用姿势

BRPOPLPUSH

更多

微信公众号:CodePlayer