「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!」
你必须非常努力,才能看起来毫不费力!
微信搜索公众号[ 漫漫Coding路 ],一起From Zero To Hero !
前言
上篇文章中,我们介绍了Redis的部分命令,本篇文章我们继续学习剩余命令,如果对上次命令淡忘的小伙伴可以先复习下哟。
LLEN
可用版本:>= 1.0.0
时间复杂度: O(1)
命令格式
LLEN key
命令描述
- 返回列表长度
- 如果列表不存在,返回0
- 如果key对应的value类型不是列表,返回error
返回值
整数:列表长度
示例
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为移除元素个数
命令格式
LREM key count element
命令描述
- 根据给定的参数
element,移除列表中前count个与element相等的元素 - count>0: 从表头至表尾顺序移除
- count<0: 从表尾至表头顺序移除
- count=0: 移除列表中所有与element相等的元素
LREM list -2 "hello"相当于移除list中最后的两个hello值
返回值
整数:被移除的元素个数(key不存在时,返回0)
示例
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)
命令格式
LINSERT key BEFORE|AFTER pivot element
命令描述
- 将一个元素值
element,插入到列表中指定元素pivot的之前或之后 - key不存在时,不执行任何操作
- 如果key对应的value类型不是列表,返回error
返回值
整数:插入元素后列表长度(如果未找到 pivot,则返回 -1 )
示例
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)
命令格式
LSET key index element
命令描述
- 将索引为
index的值设置为element - 索引超出列表范围,返回error
返回值
字符串:“OK”
示例
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为指定元素在列表中的个数。
命令格式
LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len]
命令描述
- 返回指定元素
element在列表中的索引 - 没有指定可选参数时,默认从表头搜索至表尾,返回第一个匹配的索引;如果没有匹配到,返回 nil
> RPUSH mylist a b c 1 2 3 c c
> LPOS mylist c
2
- 可选参数
rank:当列表中有多个匹配时,返回第rank个匹配的索引。当rank为正数时,从表头搜索至表尾返回索引;当rank为负数时,从表尾搜索至表头 - 无论
rank是正数或者负数,返回的索引始终是正数,即以0开始的索引值
# 返回第二个匹配的索引
> LPOS mylist c RANK 2
6
# 从表尾开始搜索,返回第一个匹配的索引
> LPOS mylist c RANK -1
7
- 可选参数
count: 返回多个匹配到的索引;count=0 表示返回所有匹配索引
> LPOS mylist c COUNT 2
[2,6]
> LPOS mylist c COUNT 0
[2,6,7]
rank+count: 从第rank个匹配到的索引开始,返回count个索引
> LPOS mylist c RANK -1 COUNT 2
[7,6]
- 当没有匹配到值时,如果提供了可选参数
count,返回空数组;否则返回nil - 可选参数
MAXLEN:至多访问列表中的MAXLEN个元素进行比较,0表示遍历所有元素比较
返回值
没有可选参数:返回元素索引 或者 nil (元素不存在)
提供count参数: 返回数组
示例
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为被移除的元素个数
命令格式
LTRIM key start stop
命令描述
- 对一个列表就行截取,保留给定范围内的元素,其余元素会被删除
start、stop可以是正数或者负数:正数表示从表头开始(索引为0),负数表示从表尾开始(索引为-1)- 给定参数超出列表范围不会报错:
start超出列表范围或者start > stop,结果是空列表stop超出列表范围,处理完元素就结束
返回值
字符串:“OK”
示例
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整形
命令格式
BLPOP key [key ...] timeout
命令描述
BLPOP是列表的阻塞式弹出原语BLPOP是LPOP命令的阻塞版本,当给定的列表中没有元素可以弹出时,会一直阻塞住,直到列表中有元素可以弹出或者阻塞时间超时。当指定了多个列表时,会按照顺序弹出第一个非空的列表元素
- 非阻塞行为
- 当调用BPOP时,会按照顺序检查列表是否非空,返回第一个非空列表名称和其表头元素值。
- 当调用
BLPOP list1 list2 list3 0时, 如果list1为空,list2 list3非空,那么会返回list2以及其表头元素
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
# 列表不存在,指定了timeout=10, 10s后返回nil
127.0.0.1:6379> blpop notexistslist 10
(nil)
(10.06s)
# 客户端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
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 命令:
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
# 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. 执行事务时,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。
LOOP forever
WHILE SPOP(key) returns elements
... process elements ...
END
BLPOP helper_key
END
生产者:生产数据时,同时添加到set和list中
MULTI
SADD key element
LPUSH helper_key x
EXEC
BRPOP
可用版本:>= 2.0.0
时间复杂度:O(N),N为命令提供key的个数
6.0版本后,timeout参数为double浮点类型,而非integer整形
命令格式
BRPOP key [key ...] timeout
命令描述
BRPOP是列表的阻塞式弹出原语BLPOP是RPOP命令的阻塞版本,当没有元素可以弹出时会一直阻塞;列表非空时从队尾弹出。- 具体命令描述见
BLPOP
返回值
nil: 列表无数据且超时
数组:获取到数据,数组第一个元素为列表名,第二个为元素值
示例
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,见下个命令
命令格式
BRPOPLPUSH source destination timeout
命令描述
BRPOPLPUSH是RPOPLPUSH的阻塞版本- 当
source中无数据时,会一直阻塞;当有数据时,将source表尾数据插入至destination表头
返回值
nil: 操作超时
字符串:弹出的元素值
示例
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,见下个命令
命令格式
BLMOVE source destination LEFT|RIGHT LEFT|RIGHT timeout
命令描述
BLMOVE是LMOVE的阻塞版本- 当
source中无数据时,会一直阻塞,其余操作和LMOVE一致
返回值
nil: 操作超时
字符串:弹出的元素值
示例
# 客户端1阻塞
client 1> blmove sourcelist destlist right left 0
"hello"
(14.21s)
# 客户端2插入数据后,客户端1返回数据
client 2> lpush sourcelist hello
(integer) 1
使用姿势
见 BRPOPLPUSH
更多
个人博客: lifelmy.github.io/
微信公众号:漫漫Coding路