列表简介
Redis的列表(list)是一种线性的有序结构(类似于Java中的List集合),可以按照元素的推入列表中的顺序来存储元素,这些元素既可以是文字数据,也可以是二进制数据,并且列表中的元素是可以重复的。
存储结构
Redis的列表键支持左右两端推入(PUSH)或弹出(POP)数据,如下图所示:
列表支持的功能
Redis为列表提供了丰富的操作命令,通过这些命令,用户可以:
- 将新元素推入列表的左端或者右端
- 移除位于列表最左端或者最右端的元素
- 移除列表最右端的元素,然后把被移除的元素推入另一个列表的左端
- 获取列表包含的元素数量
- 获取列表在指定索引上的单个元素,或者获取列表在指定索引范围内的多个元素
- 为列表的指定索引设置新元素,或者把新元素添加到某个指定元素的前面或后面
- 对列表进行剪裁,只保留指定索引范围内的元素
- 从列表中移除指定的元素
- 执行能够阻塞客户端的推入和移除操作
列表命令速查表
以下表格列举出了Redis针对列表类型键提供的操作命令、用法、参数及其说明:
命令 | 用法及参数 | 说明 |
---|---|---|
LPUSH | LPUSH list item [item item ...] | 将一个或多个元素推入给定列表的左端 |
RPUSH | RPUSH list item [item item ...] | 将一个或多个元素推入给定列表的右端 |
LPUSHX | LPUSHX list item | 只在列表存在的情况下将一个元素推入列表左端 |
RPUSHX | RPUSHX list item | 只在列表存在的情况下将一个元素推入列表右端 |
LPOP | LPOP list | 从列表最左端弹出一个元素 |
RPOP | RPOP list | 从列表最右端弹出一个元素 |
RPOPLPUSH | RPOPLPUSH source target | 从源列表最右端弹出一个元素,然后推入目标列表的最左端 |
LLEN | LLEN list | 获取列表长度,即列表中元素个数 |
LINDEX | LINDEX list index | 获取指定索引的元素 |
LRANGE | LRANGE list start end | 获取指定索引范围内的元素 |
LSET | LSET list index new_element | 为指定索引设置新元素 |
LINSERT | LINSERT list BEFORE|AFTER target_element new_element | 将一个新元素插入到列表某个指定元素的前面或后面 |
LTRIM | LTRIM list start end | 移除给定索引范围以外的元素,只保留给定范围内的元素 |
LREM | LREM list count element | 移除列表中的指定的元素,移除规则与 count参数有关,具体参看命令详解 |
BLPOP | BLPOP list [list ...] timeout | 带阻塞功能的左端弹出操作 |
BRPOP | BRPOP list [list ...] timeout | 带阻塞功能的右端弹出操作 |
BRPOPLPUSH | BRPOPLPUSH source target timeout | RPOPLPUSH命令的阻塞版本 |
命令详解
LPUSH命令
用户可以通过LPUSH命令,将一个或多个元素推入指定列表的左端:
LPUSH list item [item item ...]
推入操作执行成功后,LPUSH命令会以当前列表包含的元素数量作为返回值。
127.0.0.1:6379> LPUSH fruits "Apple"
(integer) 1
127.0.0.1:6379> LPUSH fruits "Banana"
(integer) 2
LPUSH命令执行过程如下图所示:
如果给定多个元素,那么LPUSH命令将按照顺序依次将元素推入列表左端。
复杂度:
O
(
N
)
O(N)
O(N),其中
N
N
N为被推入列表的元素数量。
版本要求:LPUSH命令从Redis 1.0.0版本开始可用,但是只有Redis 2.4.0或以上版本才支持一次推入多个元素。
RPUSH命令
RPUSH命令和LPUSH命令类似,这两个命令执行的都是元素推入操作,LPUSH命令会将元素推入列表的左端,RPUSH命令会将元素推入列表右端。
RPUSH list item [item item ...]
RPUSH命令执行完毕后,会返回列表当前包含的元素数量作为返回值。
127.0.0.1:6379> RPUSH fruits "Apple"
(integer) 1
127.0.0.1:6379> RPUSH fruits "Banana"
(integer) 2
RPUSH命令执行过程如下图所示:
如果给定多个元素,那么RPUSH命令将按照顺序依次将元素推入列表右端。
复杂度:
O
(
1
)
O(1)
O(1),其中
N
N
N为被推入列表的元素数量。
版本要求:RPUSH命令从Redis 1.0.0版本开始可用,但是只有Redis 2.4.0或以上版本支持一次推入多个元素。
LPUSHX、RPUSHX命令
当用户调用LPUSH命令或RPUSH命令尝试将元素推入列表时,如果给定的列表不存在,那么命令将自动创建一个空列表,并将元素推入列表中,如下所示:
127.0.0.1:6379> LPUSH list1 "item1"
(integer) 1
除了LPUSH命令和RPUSH命令以外,Redis还提供了LPUSHX命令和RPUSHX命令:
LPUSHX list item
RPUSHX list item
这两个命令对待空列表的方式与LPUSH和RPUSH命令正好相反:
- LPUSHX命令只会在列表已经存在的情况下,将元素推入列表左端。
- RPUSHX命令只会在列表已经存在的情况下,将元素推入列表右端。
如果给定列表并不存在,那么LPUSHX命令和RPUSHX命令将放弃执行推入操作。
如果命令执行成功后,将返回列表当前的长度作为返回值,如果推入操作未能成功执行,那么命令将返回0作为结果。
假设list2列表不存在,执行如下操作:
127.0.0.1:6379> LPUSHX list2 "item-x"
(integer) 0 -- 没有推入任何元素
127.0.0.1:6379> RPUSHX list2 "item-y"
(integer) 0 -- 没有推入任何元素
127.0.0.1:6379> LPUSH list2 "item"
(integer) 1 -- 推入一个元素,使得列表变为非空
127.0.0.1:6379> LPUSHX list2 "item-x"
(integer) 2 -- 执行推入操作之后,列表包含2个元素
127.0.0.1:6379> RPUSHX list2 "item-y"
(integer) 3
注意:与LPUSH命令和RPUSH命令不一样,LPUSHX命令和RPUSHX命令每次只能推入一个元素,尝试推入多个元素将引发错误:
127.0.0.1:6379> LPUSHX list "item1" "item2" "item3"
(error) ERR wrong number of arguments for 'lpushx' command
复杂度:
O
(
1
)
O(1)
O(1)
版本要求:LPUSHX命令和RPUSHX命令从Redis 2.2.0版本开始可用。
LPOP命令
用户可以通过LPOP命令移除位于列表最左端的元素,并将被移除的元素返回给用户:
LPOP list
127.0.0.1:6379> LPOP fruits
"Apple"
127.0.0.1:6379> LPOP fruits
"Banana"
127.0.0.1:6379> LPOP fruits
(nil)
LPOP命令执行过程如下图所示:
复杂度:
O
(
1
)
O(1)
O(1)
版本要求:LPOP命令从Redis 1.0.0版本开始可用。
RPOP命令
与LPOP命令类似,RPOP命令可以移除位于列表最右端的元素,并将被移除的元素返回给用户:
RPOP list
127.0.0.1:6379> RPOP fruits
"Banana"
127.0.0.1:6379> RPOP fruits
"Apple"
127.0.0.1:6379> RPOP fruits
(nil)
RPOP命令执行过程如下图所示:
复杂度:
O
(
1
)
O(1)
O(1)
版本要求:RPOP命令从Redis 1.0.0版本开始可用。
RPOPLPUSH命令
RPOPLPUSH命令的行为和它的名字一样,首先使用RPOP命令将源列表最右端的元素弹出,然后使用LPUSH命令将被弹出的元素推入目标列表左端,使之称为目标列表的最左端元素:
RPOPLPUSH source target
RPOPLPUSH命令会返回被弹出的元素作为结果。
127.0.0.1:6379> RPUSH list1 "a" "b" "c"
(integer) 3
127.0.0.1:6379> RPUSH list2 "d" "e" "f"
(integer) 3
127.0.0.1:6379> RPOPLPUSH list1 list2
"c"
127.0.0.1:6379> RPOPLPUSH list1 list2
"b"
127.0.0.1:6379> RPOPLPUSH list1 list2
"a"
RPOPLPUSH命令的执行过程如下图所示:
源列表和目标列表相同
RPOPLPUSH命令允许用户将源里边和目标列表设置为同一个列表,在这种情况下,RPOPLPUSH命令的效果相当于将列表最右端的元素变成列表最左端的元素,如下所示:
127.0.0.1:6379> RPUSH list "a" "b" "c"
(integer) 3
127.0.0.1:6379> RPOPLPUSH list list
"c"
127.0.0.1:6379> RPOPLPUSH list list
"b"
127.0.0.1:6379> RPOPLPUSH list list
"a"
处理空列表
如果传给RPOPLPUSH命令的源列表不存在,那么命令将放弃执行弹出和推入操作,只返回一个空值表示失败:
127.0.0.1:6379> RPOPLPUSH list-x list-y
(nil)
复杂度:
O
(
1
)
O(1)
O(1)
版本要求:RPOPLPUSH命令从Redis 1.2.0版本开始可用。
LLEN命令
用户可以通过执行LLEN命令来获取列表长度,即列表中包含的元素个数:
LLEN list
127.0.0.1:6379> LLEN fruits
(integer) 2
复杂度:
O
(
1
)
O(1)
O(1)
版本要求:LLEN命令从Redis 1.0.0版本开始可用。
LINDEX命令
Redis列表包含的每个元素都有与之对应的正是索引和负数索引:
- 正数索引从列表的最左端计算,依次向右端递增:最左端元素的索引为0,左端第二个索引为 1,以此类推。最大的正数索引为列表长度减1,即 N − 1 N-1 N−1。
- 负数索引从列表的最右端开始计算,依次向左递减:最右端元素的索引为 -1,倒数第二个为 -2 以此类推。最大的负数索引为列表长度的负数,即
−
N
-N
−N。
为了让用户可以方便地取得索引对应的元素,Redis提供了LINDEX命令:
LINDEX list index
LINDEX命令接受一个列表和一个索引作为参数,然后返回列表在给定索引上的元素,其给定的索引即可以是正数,也可以是负数。
127.0.0.1:6379> RPUSH fruits "Apple" "Banana"
(integer) 2
127.0.0.1:6379> LINDEX fruits 0
"Apple"
127.0.0.1:6379> LINDEX fruits 1
"Banana"
127.0.0.1:6379> LINDEX fruits -1
"Banana"
127.0.0.1:6379> LINDEX fruits -2
"Apple"
如果给定的索引超出了索引范围,那么将返回一个空值(nil)。
复杂度:
O
(
N
)
O(N)
O(N),其中
N
N
N为列表长度。
版本要求:LINDEX命令从Redis 1.0.0版本开始可用。
LRANGE命令
除了可以使用LINDEX命令获取给定索引上的单个元素之外,还可以使用LRANGE命令获取给定索引范围上的多个元素:
LRANGE list start end
LRANGE命令接受一个列表、一个开始索引和一个结束索引作为参数,然后依次返回列表从开始索引到结束索引范围内的所有元素,其中开始索引和结束索引对应的元素也包含在命令返回的结果中。
127.0.0.1:6379> LRANGE letters 0 3 -- 获取列表索引0~3上的所有元素
1) "a"
2) "b"
3) "c"
4) "d"
获取列表包含的所有元素
一个快捷地获取列表所有元素的方法,就是使用0作为起始索引、-1作为结束索引去调用LRANGE命令,这种方法非常适合查看长度较短的列表:
127.0.0.1:6379> LRANGE letters 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
7) "g"
处理超出范围的索引
与LINDEX一样,LRANGE命令也需要处理超出范围的索引:
- 如果用户给定的起始索引和结束索引都超出了范围,那么LRANGE命令将返回空列表作为结果。
- 如果用户给定的其中一个索引超出了范围,那么LRANGE命令将对超出范围的索引进行修正,然后再执行实际的范围获取操作;其中超出范围的起始索引会被修正为0,而超出范围的结束索引则会被修正为-1。
127.0.0.1:6379> LRANGE letters 50 100
(empty list or set)
127.0.0.1:6379> LRANGE letters -100 -50
(empty list or set)
127.0.0.1:6379> LRANGE letters -100 5
1) "a" -- 位于索引0上的元素
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
127.0.0.1:6379> LRANGE letters 5 100
1) "f" --位于索引5上的元素
2) "g"
3) "h" --位于索引-1上的元素
复杂度:
O
(
N
)
O(N)
O(N),其中
N
N
N为给定列表的长度。
版本要求:LRANGE命令从Redis 1.0.0开始可用。
LSET命令
用户可以同LSET命令,为列表的指定索引设置新元素:
LSET list index new_element
LSET命令在设置成功时返回OK。
127.0.0.1:6379> LRANGE letters 0 -1
1) "a"
2) "b"
3) "c"
127.0.0.1:6379> LSET letters 1 "d"
OK
127.0.0.1:6379> LRANGE letters 0 -1
1) "a"
2) "d"
3) "c"
处理超出范围的索引
因为LSET命令只能对列表中已存在的索引进行设置,所以如果用户给定的索引超出了列表的有效索引范围,那么LSET命令将返回一个错误:
127.0.0.1:6379> LSET letters 100 "w"
(error) ERR index out of range
复杂度:
O
(
N
)
O(N)
O(N),其中
N
N
N为给定列表的长度
版本要求:LSET命令从Redis 1.0.0开始可用。
LINSERT命令
通过使用LINSERT命令,用户可以将一个新元素插入列表某个指定元素的前面或后面:
LINSERT list BEFORE|AFTER target_element new_element
LINSERT命令第二个参数值可以是BEFORE或者AFTER,分别用于指示命令将新元素插入到目标元素的前面或者后面,插入执行成功后返回当前列表的长度。
127.0.0.1:6379> LRANGE list 0 -1
1) "a"
2) "b"
3) "c"
127.0.0.1:6379> LINSERT list BEFORE "b" "10086"
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "a"
2) "10086"
3) "b"
4) "c"
127.0.0.1:6379> LINSERT list AFTER "c" "12345"
(integer) 5
127.0.0.1:6379> LRANGE list 0 -1
1) "a"
2) "10086"
3) "b"
4) "c"
5) "12345"
处理不存在的元素
LINSERT命令要求用户给定的目标元素必须存在于列表当中。如果给定的目标元素不存在,那么LINSERT命令将返回 -1 表示插入失败:
127.0.0.1:6379> LINSERT list BEFORE "not-exists-element" "new element"
(integer) -1
复杂度:
O
(
N
)
O(N)
O(N),其中
N
N
N为列表的长度。
版本要求:LINSERT命令从Redis 2.2.0版本开始可用。
LTRIM命令
LTRIM命令接受一个列表和一个索引范围作为参数,并移除列表中索引范围以外的元素,只保留给定范围内的元素:
LTRIM list start end
LTRIM命令执行成功后,返回OK。
127.0.0.1:6379> RPUSH letters "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k"
(integer) 11
127.0.0.1:6379> LTRIM letters 0 6
OK
127.0.0.1:6379> LRANGE letters 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
7) "g"
127.0.0.1:6379> LTRIM letters 3 5
OK
127.0.0.1:6379> LRANGE letters 0 -1
1) "d"
2) "e"
3) "f"
LTRIM命令执行过程如下图所示:
处理负数索引
与LRANGE命令一样,LTRIM命令不仅可以处理正数索引,还可以处理负数索引:
127.0.0.1:6379> RPUSH numbers 0 1 2 3 4 5 6 7 8 9
(integer) 10
127.0.0.1:6379> LTRIM numbers -5 -1
OK
127.0.0.1:6379> LRANGE 0 -1
1) "5"
2) "6"
3) "7"
4) "8"
9) "9"
复杂度:
O
(
N
)
O(N)
O(N),其中
N
N
N为列表的长度
版本要求:LTRIM命令从Redis 1.0.0版本开始可用。
LREM命令
用户可以通过LREM命令移除列表中指定的元素:
LREM list count element
count参数的值决定了LREM命令移除元素的方式:
- 如果count参数的值等于0,那么LREM命令将移除列表中包含的所有指定的元素;
- 如果count参数的值大于0,那么LREM命令将从列表左端开始向右进行检查,并移除最先发现的count个指定元素;
- 如果count参数的值小于0,那么LREM命令将从列表的右端开始向左进行检查,并移除最先发现的count个指定元素。
LREM命令执行完成后,返回被移除的元素个数。
127.0.0.1:6379> RPUSH sample1 "a" "b" "b" "a" "c" "c" "a"
(integer) 7
127.0.0.1:6379>RPUSH sample2 "a" "b" "b" "a" "c" "c" "a"
(integer) 7
127.0.0.1:6379> RPUSH sample3 "a" "b" "b" "a" "c" "c" "a"
(integer) 7
127.0.0.1:6379> LREM sample1 0 "a"
(integer) 3 -- 移除了3个"a"元素
127.0.0.1:6379> LRANGE sample1 0 -1
1) "b"
2) "b"
3) "c"
4) "c"
127.0.0.1:6379> LREM sample2 2 "a"
(integer) 2 -- 移除了2个"a"元素
127.0.0.1:6379> LRANGE sample2 0 -1
1) "b"
2) "b"
3) "c"
4) "c"
5) "a"
因为上面的LREM命令只要求移除最先发现的2个"a"元素,所以位于最右端的“a”元素没有被移除,执行过程如下图所示:
当count<0时,如下所示:
127.0.0.1:6379> LREM sample3 -2 "a"
(integer) 2
127.0.0.1:6379> LRANGE sample3 0 -1
1) "a"
2) "b"
3) "b"
4) "c"
5) "c"
当count<0时,执行过程如下图所示:
复杂度:
O
(
N
)
O(N)
O(N),其中
N
N
N为列表的长度。
版本要求:LREM命令从Redis 1.0.0版本开始可用。
BLPOP命令
BLPOP命令是带有阻塞功能的左端弹出操作,它接受任意多个列表及一个秒级精度的超时时限作为参数:
BLPOP list [list ...] timeout
BLPOP命令会按照从左到右的顺序依次检查用户给定的列表,并对最先遇到的非空列表执行左端弹出操作。如果BLPOP命令在检查完给定列表后都没有找到可以执行弹出操作的非空列表,那么将阻塞执行该命令的客户端并开始等待,直到某个给定列表变为非空,或者等待时间超出给定的时限为止。
当BLPOP命令成功对某个非空列表执行了弹出操作后,将返回一个包含两个元素的数组:数组的第一个元素记录了执行弹出操作的列表,即被弹出元素的来源列表,数组的第二个元素则是被弹出元素本身。
127.0.0.1:6379> BLPOP letters 5 -- 尝试弹出letters列表最左端的元素,最多阻塞5s
1) "letters"
2) "a"
解除阻塞状态
当BLPOP命令发现用户给定的所有列表都为空时,就会让执行命令的客户端进入阻塞状态。如果在客户端被阻塞的过程中,有另一个客户端向导致阻塞的列表推入了新的元素,那么该列表就会变为非空,而被阻塞的客户端也会随着BLPOP命令成功弹出列表元素而重新回到非阻塞状态。
下表展示了客户端A从被阻塞到解除阻塞的整个过程:
时间 | 客户端A | 客户端B |
---|---|---|
T1 | 执行BLPOP list 10,因为list为空导致客户端被阻塞 | |
T2 | 执行RPUSH list "hello"命令,将"hello"元素推入列表 | |
T3 | 服务器检测到导致客户端阻塞的list列表已经非空,于是从列表弹出"hello"元素并返回给客户端 | |
T4 | 接受到"hello"元素的客户端重新回到非阻塞状态 |
如果在同一时间有多个客户端因为同一个列表而被阻塞,那么当导致阻塞的列表变为非空时,服务器将按照 “先阻塞先服务” 的规则,依次为被阻塞的客户端弹出列表元素。
处理空列表
如果用户想BLPOP命令传入的所有列表都是空列表,并且这些列表在给定的时限内一直没有变成非空列表,那么BLPOP命令将在给定时限到达之后向客户端返回一个空值,表示没有任何元素被弹出:
127.0.0.1:6379> BLPOP empty-list 5
(nil)
(5.04s)
列表名的作用
BLPOP命令之所以返回被弹出元素的来源列表,是为了在传入多个列表的情况下,让用户知道被弹出元素来自于哪个列表。
阻塞效果的范围
BLPOP命令的阻塞效果只对执行该命令的客户端有效,其他客户端及Redis服务器本身并不会因为这个命令而被阻塞。
复杂度:
O
(
N
)
O(N)
O(N),其中
N
N
N为为用户给定的列表数量。
版本要求:BLPOP命令从Redis 2.0.0版本开始可用。
BRPOP命令
与BLPOP命令类似,BRPOP命令是带有阻塞功能的右端弹出操作,除了弹出元素的方向不同,其他与BLPOP命令完全一致。
BRPOP list [list ..] timeout
复杂度:
O
(
N
)
O(N)
O(N),其中
N
N
N为为用户给定的列表数量。
版本要求:BRPOP命令从Redis 2.0.0版本开始可用。
BRPOPLPUSH命令
BRPOPLPUSH命令是RPOPLPUSH命令的阻塞版本,BRPOPLPUSH命令接受一个源列表、一个目标列表以及一个秒级精度的超时实现为参数:
BRPOPLPUSH source target timeout
根据源列表是否为空,BRPOPLPUSH命令会产生以下两种行为:
- 如果源列表为非空,那么BRPOPLPUSH命令的行为就和RPOPLPUSH命令的行为一样。
- 如果源列表为空,那么BRPOPLPUSH命令将阻塞执行该命令的客户端,然后在给定时限内等待可弹出的元素出现,或者等待时间超过给定时限为止。
复杂度:
O
(
1
)
O(1)
O(1)
版本要求:BRPOPLPUSH命令从Redis 2.2.0版本开始可用。