前言
上篇文章中,我们介绍了Redis的部分命令,本篇文章我们继续学习剩余命令,如果对上次命令淡忘的小伙伴可以先复习下哟。
LLEN
可用版本:>= 1.0.0
时间复杂度: O(1)
命令格式
命令描述
- 返回列表长度
- 如果列表不存在,返回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为移除元素个数
命令格式
命令描述
- 根据给定的参数
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)
命令格式
命令描述
- 将索引为
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为被移除的元素个数
命令格式
命令描述
- 对一个列表就行截取,保留给定范围内的元素,其余元素会被删除
start
、 stop
可以是正数或者负数:正数表示从表头开始(索引为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
是列表的阻塞式弹出原语
BLPOP
是 LPOP
命令的阻塞版本,当给定的列表中没有元素可以弹出时,会一直阻塞住,直到列表中有元素可以弹出或者阻塞时间超时。当指定了多个列表时,会按照顺序弹出第一个非空的列表元素
- 非阻塞行为
- 当调用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"
|
- 阻塞行为
- 当给定的
key
都不存在时,即列表不存在,BLPOP
会一直阻塞,直到有其他客户端执行了 LPUSH
或 RPUSH
命令创建了列表
- 当列表中有值后,
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
|
- 优先级问题
BLPOP命令指定了多个列表,会按照从左到右的顺序,返回第一个非空列表中的元素。假设执行 BLPOP key1 key2 key3 key4 0
, key2
和key4
非空,那么会返回 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)
|
- 多个元素
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
|
- 在MULTI/EXEC事务中使用BLPOP
在MULTI、EXEC
事务中执行BLPOP
没有意义,因为事务会阻塞其他命令执行 LPUSH
、RPUSH
等命令,这样事务就一直阻塞在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一起使用
消费者: 使用SPOP
从set
中弹出元素,如果没有元素就阻塞到BLPOP
命令。当有数据到来时,BLPOP
停止阻塞, SPOP
开始遍历set
。
1
2
3
4
5
6
|
LOOP forever
WHILE SPOP(key) returns elements
... process elements ...
END
BLPOP helper_key
END
|
生产者:生产数据时,同时添加到set
和list
中
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
是列表的阻塞式弹出原语
BLPOP
是 RPOP
命令的阻塞版本,当没有元素可以弹出时会一直阻塞;列表非空时从队尾弹出。
- 具体命令描述见
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
|
命令描述
BRPOPLPUSH
是 RPOPLPUSH
的阻塞版本
- 当
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会删除该数据,此时数据只在我们的客户端中存在,如果此时客户端突然宕机了,那么这条数据就会丢失。为了防止数据丢失,我们可以使用RPOPLPUSH
或者 BRPOPLPUSH
,在弹出元素的同时将元素加入到另一个列表中用于备份,客户端处理完本条后将备份列表中的元素删除。
- 循环队列
通过使用相同的 key
作为 RPOPLPUSH
或 BRPOPLPUSH
命令的两个参数,客户端可以一个接一个地获取列表元,最终取得列表的所有元素,而不必像 LRANGE
命令那样一下子将所有列表元素都从服务器传送到客户端中。
以上的模式甚至在以下的两个情况下也能正常工作:
- 多个客户端同时对同一个列表进行旋转,它们获取不同的元素,直到所有元素都被读取完,之后又从头开始。
- 有其他客户端向列表尾部添加新元素。
比如我们要实现一个监控报警系统,多个服务器尽可能快速的检查多个网站是否正常,可以将多个网站存入列表中,多个服务器同时对该列表进行旋转处理。
BLMOVE
可用版本:>= 6.2.0
时间复杂度: O(1)
自6.2.0,该命令已废弃,建议使用 BLMOVE,见下个命令
命令格式
1
|
BLMOVE source destination LEFT|RIGHT LEFT|RIGHT timeout
|
命令描述
BLMOVE
是 LMOVE
的阻塞版本
- 当
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