Redis 数据类型 List

Redis列表(Lists)是简单的字符串列表,并根据插入顺序进行排序。一个Redis列表中最多可存储232-1(40亿)个元素。

Redis的列表和JavaLinkedList类似,注意它是链表而不是数组。这意味着List的插入和删除操作非常快,但是索引定位很慢。
当列表移除了最后一个元素之后,该key会被自动被删除,内存被回收。

Redis的列表结构常用来做异步队列使用。将需要延后处理的任务结构体序列化成字符串塞进列表,另一个线程从这个列表中读取数据进行处理。

存取

LPUSH

将一个或多个值value插入到列表key的头部。

1
LPUSH key value [value ...]

如果有多个value,那么从左到右依次插入列表。如果key不存在,首先会创建一个空列表再执行LPUSH操作。
命令执行成功,返回列表的长度。如果key存在,但不是List类型,会返回一个错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 加入单个元素
redis> LPUSH languages python
(integer) 1
# 加入重复元素
redis> LPUSH languages python
(integer) 2
redis> LRANGE languages 0 -1 # 列表允许重复元素
1) "python"
2) "python"
# 加入多个元素
redis> LPUSH mylist a b c
(integer) 3
redis> LRANGE mylist 0 -1
1) "c"
2) "b"
3) "a"

LPUSHX

LPUSHXLPUSH相同,不同的是,LPUSHX一次只能插入一个value,而且只有当key存在且是List类型时,才会将值value插入到列表key的头部。
如果key不存在,则不执行操作。

1
LPUSHX key value

命令执行成功,返回列表的长度。如果key存在,但不是List类型,会返回一个错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 对空列表执行 LPUSHX
redis> LLEN greet
(integer) 0
redis> LPUSHX greet "hello" # LPUSHX 失败,因为列表为空
(integer) 0
# 对非空列表执行 LPUSHX
redis> LPUSH greet "hello" # 这次 LPUSHX 执行成功
(integer) 1
redis> LPUSHX greet "good morning"
(integer) 2
redis> LRANGE greet 0 -1
1) "good morning"
2) "hello"
# 非列表类型,返回错误
redis> set key value
OK
redis> lpush key xxx
(error) WRONGTYPE Operation against a key holding the wrong kind of value

RPUSH

RPUSH是将一个或多个值value插入到列表key的尾部。

1
RPUSH key value [value ...]

如果key不存在,会首先创建一个空列表,再执行RPUSH
返回执行RPUSH后列表的长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 添加单个元素
redis> RPUSH languages c
(integer) 1
# 添加重复元素
redis> RPUSH languages c
(integer) 2
redis> LRANGE languages 0 -1 # 列表允许重复元素
1) "c"
2) "c"
# 添加多个元素
redis> RPUSH mylist a b c
(integer) 3
redis> LRANGE mylist 0 -1
1) "a"
2) "b"
3) "c"

RPUSHX

RPUSHXRPUSH相同,不同的是,RPUSHX一次只能插入一个value,而且只有当key存在且是List类型时,才会将值value插入到列表key的尾部。
如果key不存在,则不执行操作。

1
RPUSHX key value

返回执行RPUSH后列表的长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# key不存在
redis> LLEN greet
(integer) 0
# 对不存在的 key 进行 RPUSHX,PUSH 失败。
redis> RPUSHX greet "hello"
(integer) 0
# key 存在且是一个非空列表
# 先用 RPUSH 插入一个元素
redis> RPUSH greet "hi"
(integer) 1
# greet 是一个列表类型,RPUSHX 操作成功
redis> RPUSHX greet "hello"
(integer) 2
redis> LRANGE greet 0 -1
1) "hi"
2) "hello"

LINSERT

value插入key中指定pivot元素的前面或后面。
如果pivotkey不存在则不执行任何操作。

1
LINSERT key BEFORE|AFTER pivot value

操作成功,返回插入之后,列表的长度。pivot不存在,返回-1。如果key不存在或为空,则返回0,如果key不是一个列表类型,则返回一个错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
redis> RPUSH mylist "Hello"
(integer) 1
redis> RPUSH mylist "World"
(integer) 2
redis> LINSERT mylist BEFORE "World" "There"
(integer) 3
redis> LRANGE mylist 0 -1
1) "Hello"
2) "There"
3) "World"
# 对一个非空列表插入,查找一个不存在的 pivot
redis> LINSERT mylist BEFORE "go" "let's"
(integer) -1 # 失败
# 对一个空列表执行 LINSERT 命令
redis> EXISTS fake_list
(integer) 0
redis> LINSERT fake_list BEFORE "nono" "gogogog"
(integer) 0 # 失败

LPOP

返回key列表中的头元素。

1
LINSERT key BEFORE|AFTER pivot value

如果key不存在,则返回nil

1
2
3
4
5
6
7
8
redis> LLEN course
(integer) 0
redis> RPUSH course algorithm001
(integer) 1
redis> RPUSH course c++101
(integer) 2
redis> LPOP course # 移除头元素
"algorithm001"

BLPOP

BLPOPLPOP类似,但是BLPOP在列表为空时,当前连接会被BLPOP阻塞,直到超时或有另一个客户端PUSH了可弹出的元素为止。
当指定多个key参数时,会按key的先后顺序依次检查各个列表,并弹出第一个非空列表的头元素。
timeout参数表示阻塞的时长,单位为秒,注意如果timeout0,表示可以无限期延长阻塞。

1
BLPOP key [key ...] timeout

如果列表为空,返回一个nil。 否则,返回一个含有两个元素的列表,第一个元素是被弹出元素所属的key,第二个元素是被弹出元素的值。

BLPOP命令的非阻塞行为

例如,现在有jobcommandrequest三个列表,job不存在,而commandrequest都是非空列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 确保key都被删除
redis> DEL job command request
(integer) 0
# 为command列表增加一个值
redis> LPUSH command "update system..."
(integer) 1
# 为request列表增加一个值
redis> LPUSH request "visit page"
(integer) 1
# job 列表为空,被跳过,紧接着 command 列表的第一个元素被弹出。
redis> BLPOP job command request 0
1) "command" # 弹出元素所属列表的key
2) "update system..." # 弹出元素的值

BLPOP命令的阻塞行为

当指定的所有key都不存在或包含空列表,BLPOP命令将阻塞连接,直到等待超时,或有可弹出元素为止。

RPOP

移除并返回key列表的尾元素。

1
RPOP key

key不存在时,返回nil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
edis> RPUSH mylist "one"
(integer) 1
redis> RPUSH mylist "two"
(integer) 2
redis> RPUSH mylist "three"
(integer) 3
redis> RPOP mylist # 返回被弹出的元素
"three"
redis> LRANGE mylist 0 -1 # 列表剩下的元素
1) "one"
2) "two"

BRPOP

BRPOPBLPOP基本相同,不同点在于一个弹出头部元素,一个是尾部元素,而且会阻塞操作。

1
BRPOP key [key ...] timeout

注意BRPOP弹出的元素,一样会被移除。

1
2
3
4
5
6
7
8
9
10
11
12
redis> LLEN course
(integer) 0
redis> RPUSH course algorithm001
(integer) 1
redis> RPUSH course c++101
(integer) 2
redis> BRPOP course 30
1) "course" # 弹出元素的 key
2) "c++101" # 弹出元素的值

LINDEX

返回列表key中下标为index的元素。

1
LINDEX key index

index可以是负数,比如:-1表时倒数第一个元素,-2表时倒数第二个元素,以次类推。
如果index不在列表有效范围内,返回一个nil。如果key不是列表类型,返回一个错误。

1
2
3
4
5
6
7
8
9
10
11
redis> LPUSH mylist "World"
(integer) 1
redis> LPUSH mylist "Hello"
(integer) 2
redis> LINDEX mylist 0
"Hello"
redis> LINDEX mylist -1
"World"
# index不在 mylist 的区间范围内
redis> LINDEX mylist 3
(nil)

LRANGE

返回列表key中指定区间内的元素。以偏移量startstop指定的区间内的元素。

1
LRANGE key start stop

startstop索引位的元素都包含在取值范围内,比如执行LRANGE list 0 10,结果是一个包含11个元素的列表。
startstop超出范围的下标值不会引起错误。
如果start大于最大下标值end则会返回一个空列表。如果stop大于最大下标值end,会自动设置stop的值设置为end

1
2
3
4
5
6
7
8
9
10
11
12
redis> RPUSH fp-language lisp
(integer) 1
redis> LRANGE fp-language 0 0
1) "lisp"
redis> RPUSH fp-language scheme
(integer) 2
redis> LRANGE fp-language 0 1
1) "lisp"
2) "scheme"

修改列表元素

LSET

设置列表key中下标为index的元素值为value

1
LSET key index value

如果index超出范围,或对一个空列表进行设置时,会返回错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 对空列表进行 LSET
redis> EXISTS list
(integer) 0
redis> LSET list 0 item
(error) ERR no such key
# 对非空列表进行 LSET
redis> LPUSH job "cook food"
(integer) 1
redis> LRANGE job 0 0
1) "cook food"
redis> LSET job 0 "play game"
OK
redis> LRANGE job 0 0
1) "play game"
# index 超出范围
redis> LLEN list
(integer) 1
redis> LSET list 3 'out of range'
(error) ERR index out of range

RPOPLPUSH

RPOPLPUSHRPOPLPUSH两个操作的合并,会执行两个原子操作:

  • 将列表source的尾元素弹出,并返回给客户端。
  • source弹出的元素,作为destination列表的头元素插入。
1
RPOPLPUSH source destination

如果source不存在,返回nil。如果sourcedestination是同一个列表,就会把尾元素移动至开头,这叫做列表的旋转(rotation)操作。

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
40
41
42
43
44
45
46
# source 和 destination 不同
redis> LRANGE alpha 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
redis> RPOPLPUSH alpha reciver
"d"
redis> LRANGE alpha 0 -1
1) "a"
2) "b"
3) "c"
redis> LRANGE reciver 0 -1
1) "d"
# 再执行一次,表明 RPOP 和 LPUSH 的位置正确
redis> RPOPLPUSH alpha reciver
"c"
redis> LRANGE alpha 0 -1
1) "a"
2) "b"
redis> LRANGE reciver 0 -1
1) "c"
2) "d"
# source 和 destination 相同
redis> LRANGE number 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
redis> RPOPLPUSH number number
"4"
# 4 被旋转到了表头
redis> LRANGE number 0 -1
1) "4"
2) "1"
3) "2"
4) "3"
redis> RPOPLPUSH number number
"3"
redis> LRANGE number 0 -1
1) "3"
2) "4"
3) "1"
4) "2"

BRPOPLPUSH

BRPOPLPUSHRPOPLPUSH基本相同,BRPOPLPUSH是阻塞版本,当指定的源列表source不为空时,其表现和RPOPLPUSH一样。
source为空时,连接将被BRPOP命令阻塞,直到等待超时或有可弹出元素为止。

1
BRPOPLPUSH source destination timeout

如果指定时间内没有任何元素弹出,返回一个nil。 否则,返回一个含有两个元素的列表,其中:第一个元素是被弹出元素所属的key,第二个元素是被弹出元素的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 非空列表
redis> BRPOPLPUSH msg reciver 500
"hello moto" # 弹出元素的值
(4.31s) # 等待时长
redis> LLEN reciver
(integer) 1
redis> LRANGE reciver 0 0
1) "hello moto"
# 空列表
redis> BRPOPLPUSH msg reciver 1
(nil)
(2.24s)

其他

LLEN

返回列表key的长度。

1
LLEN key

如果key不存在,返回0。如果key不是列表类型,返回一个错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 空列表
redis> LLEN job
(integer) 0
# 非空列表
redis> LPUSH job "cook food"
(integer) 1
redis> LPUSH job "have lunch"
(integer) 2
redis> LLEN job
(integer) 2

LREM

移除元素,指定移除数量count,移除列表key中与value相等的元素。

1
LREM key count value

count的值可以有下面三种情况:

  • count > 0,从表头开始向表尾搜索,移除与value相等的元素,数量为count
  • count < 0,从表尾开始向表头搜索,移除与value相等的元素,数量为count的绝对值。
  • count = 0,移除表中所有与value相等的值。

如果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
40
41
42
43
44
45
46
47
# 先创建一个表,内容排列是
# morning hello morning helllo morning
redis> LPUSH greet "morning"
(integer) 1
redis> LPUSH greet "hello"
(integer) 2
redis> LPUSH greet "morning"
(integer) 3
redis> LPUSH greet "hello"
(integer) 4
redis> LPUSH greet "morning"
(integer) 5
redis> LRANGE greet 0 4 # 查看所有元素
1) "morning"
2) "hello"
3) "morning"
4) "hello"
5) "morning"
redis> LREM greet 2 morning # 移除从表头到表尾,最先发现的两个 morning
(integer) 2 # 两个元素被移除
redis> LLEN greet # 还剩 3 个元素
(integer) 3
redis> LRANGE greet 0 2
1) "hello"
2) "hello"
3) "morning"
redis> LREM greet -1 morning # 移除从表尾到表头,第一个 morning
(integer) 1
redis> LLEN greet # 剩下两个元素
(integer) 2
redis> LRANGE greet 0 1
1) "hello"
2) "hello"
redis> LREM greet 0 hello # 移除表中所有 hello
(integer) 2 # 两个 hello 被移除
redis> LLEN greet
(integer) 0

LTRIM

对列表key进行修剪,通过startstop指定区间,保留指定区间内的元素,其余的元素删除。
比如,执行LTRIM list 0 10,表示保留列表list的前11个元素,其余元素删除。
startstop可以是负数,如,-1表示列表的最后一个元素, -2表示列表的倒数第二个元素,以此类推。

1
LTRIM key start stop

操作成功返回OK,失败会返回错误信息。

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 1. start 和 stop 都在列表的索引范围之内
# alpha 是一个包含 5 个字符串的列表
redis> LRANGE alpha 0 -1
1) "h"
2) "e"
3) "l"
4) "l"
5) "o"
# 删除 alpha 列表索引为 0 的元素
redis> LTRIM alpha 1 -1
OK
# "h" 已被删除
redis> LRANGE alpha 0 -1
1) "e"
2) "l"
3) "l"
4) "o"
# 2. stop 大于最大下标值
# 保留 alpha 列表索引 1 至索引 10086 上的元素
redis> LTRIM alpha 1 10086
OK
# 只有索引 0 上的元素 "e" 被删除了,其他元素还在
redis> LRANGE alpha 0 -1
1) "l"
2) "l"
3) "o"
# 3. start 和 stop 都大于列表的最大下标,并且 start < stop
redis> LTRIM alpha 10086 123321
OK
redis> LRANGE alpha 0 -1 # 列表被清空
(empty list or set)
# 4. start 和 stop 都大于列表的最大下标,并且 start > stop
# 重新建立一个新列表
redis> RPUSH new-alpha "h" "e" "l" "l" "o"
(integer) 5
redis> LRANGE new-alpha 0 -1
1) "h"
2) "e"
3) "l"
4) "l"
5) "o"
# 执行 LTRIM
redis> LTRIM new-alpha 123321 10086
OK
# 同样被清空
redis> LRANGE new-alpha 0 -1
(empty list or set)