API的理解和使用——列表类型的命令
操作 | 命令 | 功能 | 时间复杂度 |
添加 | rpush key value [value ...] | 向右插入 | O(k),k是元素个数 |
lpush key value [value....] | 向左插入 | O(k),k是元素个数 | |
linsert ket before|after pivot value | 指定的元素前后插入 |
O(n),n是pivot距离列表头或尾的距离 |
|
查找 | lrange key start end | 根据范围查找 | O(s+n),s是start偏移量,n是start到end的范围 |
lindex key index | 根据索引查找某个元素 | O(n),n是索引的偏移量 | |
llen key | 获取列表长度 | O(1) | |
删除 | lpop key | 从左弹出一个元素 | O(1) |
rpop key | 从右弹出一个元素 | O(1) | |
lrem key count value | 删除指定元素 | O(n),n是列表长度 | |
ltrim key start end | 裁剪 | O(n),n是要裁剪的元素总数 | |
修改 | lset key index newvalue | 根据索引修改元素的值 | O(n),n是索引的偏移量 |
阻塞操作 | blpop key [key ...] timeout | 从左弹出阻塞 | O(1) |
brpop key [key...] timeout | 从右弹出阻塞 | O(1) |
列表(list)类型是用来存储多个有序的字符串,如下图所示,a、b、c、d、e五个元素从左到右组成了一个有序的列表,
列表中的每个字符串称为元素(element),一个列表最多可以存储2**32 -1个元素。
在Redis中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围内的元素、获取指定索引下标的元素等。
列表是一种比较灵活的数据结果,它可以充当栈和队列的角色。
列表类型有两个特点:
第一、列表种的元素是有序的,这就意味着可以通过索引下标获取某个元素或着某个范围内的元素列表。
例如要获取下图中的第5个元素,可以执行lindex user:1:message 4(索引从0算起)就可以得到元素e。
第二、列表中的元素是可以重复的。
这两个特点在后面介绍集合和有序集合后,会显得更加突出。
下面介绍列表的5个操作命令。
1.添加操作
(1)从右边值插入元素
命令:rpush key value [value ... ]
127.0.0.1:6379> rpush list:1 a b c #一直向右插入 (integer) 3 127.0.0.1:6379> lrange list:1 0 -11) "a" 2) "b" 3) "c"
(2)从左边取值插入元素
命令:lpush key value [value ... ]
127.0.0.1:6379> lpush list:2 a b c #一直向左插入 (integer) 3 127.0.0.1:6379> lrange list:2 0 -1 1) "c" 2) "b" 3) "a"
如果对于上面的插入方法还不够清晰,那么可以看下下面这个例子:
127.0.0.1:6379> lrange list:1 0 -1 1) "python" 2) "b" 127.0.0.1:6379> lpush list:1 a1 a2 a3 (integer) 5 127.0.0.1:6379> rpush list:1 c1 c2 c3 (integer) 8 127.0.0.1:6379> lrange list:1 0 -1 1) "a3" 2) "a2" 3) "a1" 4) "python" 5) "b" 6) "c1" 7) "c2" 8) "c3"
(3)向某个元素前或着后插入元素
命令:linsert key before|after pivot value
linsert命令会从列表中找到等于pivot的元素,在其前(before)或者后(after)插入一个新的元素value:
127.0.0.1:6379> linsert list:1 before b python (integer) 4 #当前长度为4 127.0.0.1:6379> lrange list:1 0 -1 1) "a" 2) "python" 3) "b" 4) "c"
2.查找
(1)获取指定范围内的元素列表
命令:lrange key start end
lrange操作会获取列表指定索引范围所有的元素。索引下标有两个特点:
第一,索引下标从左到右分别是0到N-1,但是从右到左分别是-1到-N。
第二,lrange中的end选项包含了自身,这和许多编程语言不包含end不大相同。
127.0.0.1:6379> lrange list:1 0 -1 #第一个到最后一个,就是所有 1) "a" 2) "python" 3) "b" 4) "c" 127.0.0.1:6379> lrange list:1 1 2 #第二个和第三个 1) "python" 2) "b"
(2)获取列表指定索引下标的元素
命令:lindex key index
127.0.0.1:6379> lrange list:1 0 -1 1) "a" 2) "python" 3) "b" 4) "c" 127.0.0.1:6379> lindex list:1 1 #获取第二个元素 "python"
(3)获取列表长度
命令:llen key
127.0.0.1:6379> lrange list:1 0 -1 1) "a" 2) "python" 3) "b" 4) "c" 127.0.0.1:6379> llen list:1 (integer) 4
3.删除
(1)从列表左侧弹出元素
命令:lpop key
127.0.0.1:6379> lrange list:1 0 -1 1) "a" 2) "python" 3) "b" 4) "c" 127.0.0.1:6379> lpop list:1 "a" 127.0.0.1:6379> lrange list:1 0 -1 1) "python" 2) "b" 3) "c"
(2)从列表右侧弹出元素
命令:rpop key
127.0.0.1:6379> lrange list:1 0 -1 1) "python" 2) "b" 3) "c" 127.0.0.1:6379> rpop list:1 "c" 127.0.0.1:6379> lrange list:1 0 -1 1) "python" 2) "b"
(3)删除指定元素
命令:lrem key count value
lrem命令会从列表中找到等于value的元素进行删除,根据count的不同分为三种情况:
count > 0:从左到右,删除最多count个元素,前提是要有。
count = 0:删除所有
count < 0:从右到左,删除最多count绝对值个元素
127.0.0.1:6379> lrange list:1 0 -1 1) "d" 2) "c" 3) "b" 4) "a" 5) "c" 6) "b" 7) "a" 8) "b" 9) "a" 10) "python" 11) "a" 12) "b" 13) "a" 14) "b" 15) "c" 16) "a" 17) "b" 18) "c" 19) "d" 127.0.0.1:6379> lrem list:1 3 a #从左边开始删 (integer) 3 127.0.0.1:6379> lrange list:1 0 -1 1) "d" 2) "c" 3) "b" 4) "c" 5) "b" 6) "b" 7) "python" 8) "a" 9) "b" 10) "a" 11) "b" 12) "c" 13) "a" 14) "b" 15) "c" 16) "d" 127.0.0.1:6379> lrem list:1 -3 a #从右边开始删 (integer) 3 127.0.0.1:6379> lrange list:1 0 -1 1) "d" 2) "c" 3) "b" 4) "c" 5) "b" 6) "b" 7) "python" 8) "b" 9) "b" 10) "c" 11) "b" 12) "c" 13) "d" 127.0.0.1:6379> lrem list:1 0 c #删除所有 (integer) 4 127.0.0.1:6379> lrange list:1 0 -1 1) "d" 2) "b" 3) "b" 4) "b" 5) "python" 6) "b" 7) "b" 8) "b" 9) "d"
lrem会从一个方向开始,匹配count个目标元素,如果遍历了所有元素,没有找到,那就会删除已有的。
127.0.0.1:6379> lrange list:1 0 -1 1) "d" 2) "b" 3) "b" 4) "b" 5) "python" 6) "b" 7) "b" 8) "b" 9) "d" 127.0.0.1:6379> lrem list:1 4 d (integer) 2
(4)按照索引范围修剪列表
命令:ltrim key start end
ltrim会删除范围之外的所有元素:
127.0.0.1:6379> lrange list:1 0 -1 1) "b" 2) "b" 3) "b" 4) "python" 5) "b" 6) "b" 7) "b" 127.0.0.1:6379> ltrim list:1 2 -3 #保留中间3个 OK 127.0.0.1:6379> lrange list:1 0 -1 1) "b" 2) "python" 3) "b"
4.修改
命令:lset key index newValue
127.0.0.1:6379> lrange list:1 0 -1 1) "b" 2) "python" 3) "b" 127.0.0.1:6379> lset list:1 -1 NB #将最后一个修改为NB OK 127.0.0.1:6379> lrange list:1 0 -1 1) "b" 2) "python" 3) "NB"
5.阻塞操作
命令:
blpop key [key ... ] timeout
brpop key [key ...] timeout
参数说明:
key [key ...]:多个列表的键
timeout:阻塞时间(单位为秒)
(1)列表不为空:客户端会立即返回
127.0.0.1:6379> lrange list:1 0 -1 1) "b" 2) "python" 3) "NB" 127.0.0.1:6379> blpop list:1 timeout = 3 #从左边取值 1) "list:1" 2) "b" 127.0.0.1:6379> brpop list:1 timeout = 3 #从右边取值 1) "list:1" 2) "NB" 127.0.0.1:6379> lrange list:1 0 -1 1) "python" 127.0.0.1:6379> brpop list:1 timeout = 0 1) "list:1" 2) "python"
当列表不为空的时候,不论时间设置为几秒都会立即返回一个值。
(2)列表为空:如果timeout = 3,那么客户端要等到3秒后返回,如果timeout = 0,那么客户端一直阻塞等下去:
127.0.0.1:6379> brpop list:1 timeout = 3 (nil) (3.01s) 127.0.0.1:6379> brpop list:1 timeout = 0 ...等待中...
如果在此期间添加了元素,客户端会立即返回:
#另一台机器 127.0.0.1:6379> lpush list:1 a b c (integer) 3 ——》列表 c b a #被阻塞那台 127.0.0.1:6379> brpop list:1 timeout = 0 1) "list:1" #从右边取值所以为a 2) "a" (108.39s) #已经等了100多秒
在使用brpop时,有两点需要注意。
第一,如果多个键,那么brpop会从左至右遍历键,一旦有一个键能弹出元素,客户端会立即返回:
127.0.0.1:6379> brpop list:1 list:2 list:3 timeout = 0 ...等待中...
此时在另一客户端list:3插入元素:
127.0.0.1:6379> brpop list:1 list:2 list:3 timeout = 0 1) "list:3" 2) "a" (8.90s)
在键元素的个数不大时,并不能体现brpop是从左到右遍历键。
第二,如果多个客户端对同一个键执行brpop,那么最先执行brpop命令的客户端先返回值
客户端1 127.0.0.1:6379> brpop list:1 timeout = 0 ...等着... 客户端2 127.0.0.1:6379> brpop list:1 timeout = 0 ...等着...
在另一台机器上往list:1上插入一个元素:
先请求的的会得到值,另一台等着 127.0.0.1:6379> brpop list:1 timeout = 0 1) "list:1" 2) "a1" (133.99s)