1. 列表(List
)类型
Redis 的列表(List
)类型是按照插入顺序排序的字符串链表。该类型和数据结构中的普通链表一样,我们可以在其头部(LPUSH
)和尾部(RPUSH
)添加新的元素。在插入元素时,如果该键不存在,那么将创创建新列表。如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除。
一个List
中最多可存储232-1
(40亿)个元素。操作列表元素时,如果是从链表的两头插入或删除元素,操作效率会非常高。即使列表中已存储了百万条数据,该操作也可以在常量短的时间内完成。但是,将元素插入列表中间或是删除位于链表中间元素,那操作效率会非常的低。
2. 列表类型中的命令及使用
在List
类型中,有些命令是从列表两端进行操作的,如:LPUSH
、RPUSH
等,这此命令操作效率很高。而有些需要指定操作位置上,如:LINDEX
、LRANGE
等,这些命令操作效率较低。
2.1 创建列表/插入元素
一个列表可以通过LPUSH
、RPUSH
方法创建并插入向列表的两端元素。向列表两端插入元素还可以使用LPUSHX
和RPUSHX
方法,这两方法只能向已存在的列表中插入数据。如果需要向列表的指定位置插入元素,可以使用LINSERT
方法,但该方法操作效率较低。
2.1.1 LPUSH
- 向列表头插入元素
LPUSH key value [value ...]
将一个或多个值value
插入到列表key
的头部。
如果有多个value
,那么从左到右依次插入列表。如果列表key
不存在,首先会创建一个空列表再执行LPUSH
操作。
复杂度、返回值:
- 时间复杂度:
O(1)
- 返回值:命令执行成功后,列表的长度;如果
key
存在,但不是List
类型,会返回一个错误。
使用示例
# 加入单个元素 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"
2.1.2 LPUSHX
- 当列表存在则将元素插入表头
LPUSHX key value
如果列表key
存在且是List
类型,则将值value
插入到列表key
的头部。
如果列表key
不存在,则无操作。
复杂度、返回值:
- 时间复杂度:
O(1)
- 返回值:命令执行成功后,列表的长度;如果
key
存在,但不是List
类型,会返回一个错误。
使用示例
# 对空列表执行 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
2.1.3 RPUSH
- 将指定元素插入列表末尾
RPUSH key value [value ...]
将指定的一个或多个值,依次插入列表key
的末尾。如果列表key
不存在,会首先创建一个空列表,再执行RPUSH
。
复杂度、返回值:
- 时间复杂度:
O(1)
- 返回值:执行
RPUSH
后列表的长度。
使用示例
# 添加单个元素 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"
2.1.4 RPUSHX
- 当列表存在则将元素插入表尾
RPUSHX key value
如果列表key
存在且是List
类型,则将值value
插入到列表key
的尾部。
如果列表key
不存在,则无操作。
复杂度、返回值:
- 时间复杂度:
O(1)
- 返回值:执行
RPUSHX
后列表的长度。
使用示例
# 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"
2.1.5 LINSERT
- 将元素插入指定位置
LINSERT key BEFORE|AFTER pivot value
将元素value
插入列表key
中pivot
元素的之前或之后。
如果pivot
或key
不存在则不执行任何操作。如果key
不是一个列表类型,则返回一个错误。
复杂度、返回值:
- 时间复杂度:
O(N)
,N
查找pivot
所经过元素的数量 - 返回值:操作成功,则返回插入操作完成之后,列表的长度;
pivot
不存在,则返回-1
;如果key
不存在或为空,则返回0
。
使用示例
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 # 失败
2.2 列表取值
获取列表中的元素可以使用LPOP
和RPOP
方法,这两个方法会删除并返回头/尾元素。也可以使用LINDEX
获取指定位置元素,或使用LRANGE
获取指定区间的元素。
2.2.1 LPOP
- 返回列表头元素
LPOP key
返回列表key
的头元素。
复杂度、返回值:
- 时间复杂度:
O(1)
- 返回值:列表
key
中的头元素;如果key
不存在,则返回nil
使用示例
redis> LLEN course (integer) 0 redis> RPUSH course algorithm001 (integer) 1 redis> RPUSH course c++101 (integer) 2 redis> LPOP course # 移除头元素 "algorithm001"
2.2.2 BLPOP
- 阻塞并弹出头元素
BLPOP key [key ...] timeout
BLPOP
是LPOP
命令的阻塞(blocking)版本,当指定列表中没有任何元素可供弹出的元素时,连接将被BLPOP
命令阻塞,直到等待超时或有可弹出元素为止。
当指定多个key
参数时,会按key
的先后顺序依次检查各个列表,并弹出第一个非空列表的头元素。
timeout
参数表示阻塞的时长,如果为0
表示可以无限期延长阻塞。
复杂度、返回值:
- 时间复杂度:
O(1)
- 返回值:如果列表为空,返回一个
nil
。 否则,返回一个含有两个元素的列表,其中:第一个元素是被弹出元素所属的key
,第二个元素是被弹出元素的值
。
使用示例
该命令在以下情况下是非阻塞的:
当BLPOP
命令被调用时,如果指定key
内至少有一个非空列表,那么该列表的头元素会弹出,并和key
名一起返回给调用者。
如,现在有job
、command
和 request
三个列表,其中 job
不存在,而command
和 request
都是非空列表,则:
# 确保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
命令将阻塞连接,直到等待超时,或有另外一个客户端向指定列表中的任意一个执行LPUSH
或RPUSH
命令为止。
2.2.3 RPOP
- 返回列表尾元素
LTRIM key start stop
移除并返回列表key
的尾元素。
LTRIM
使用闭区间,即:start
和stop
索引位的元素都包含在取值范围内(如:LTRIM list 0 10
结果是一个包含11
元素的列表)。
复杂度、返回值:
- 时间复杂度:
O(1)
- 返回值:列表尾元素。当
key
不存在时,返回nil
。
使用示例
redis> 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"
2.2.4 BRPOP
- 阻塞并弹出末尾元素
BRPOP key [key ...] timeout
BRPOP
是RPOP
命令的阻塞(blocking)版本,当指定列表中没有任何元素可供弹出的元素时,连接将被BRPOP
命令阻塞,直到等待超时或有可弹出元素为止。
当指定多个key
参数时,会按key
的先后顺序依次检查各个列表,并弹出第一个非空列表的末尾元素。
timeout
参数表示阻塞的时长,如果为0
表示可以无限期延长阻塞。
复杂度、返回值:
- 时间复杂度:
O(1)
- 返回值:如果列表为空,返回一个
nil
。 否则,返回一个含有两个元素的列表,其中:第一个元素是被弹出元素所属的key
,第二个元素是被弹出元素的值
。
使用示例
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" # 弹出元素的值
2.2.5 LINDEX
- 返回指定位置的元素
LINDEX key index
返回列表key
中下标为index
的元素。
index
从0
开始计数,0
表示第一个元素,1
表示第二个元素,以次类推。也可以使用负数,-1
表时倒数第一个元素,-2
表时倒数第二个元素,以次类推
复杂度、返回值:
- 时间复杂度:
O(N)
,N
为到达下标index
所经过元素的数量 - 返回值:列表中下标为
index
的元素;如果key
不是列表类型,返回一个错误。如果index
不在列表有效范围内,返回一个nil
。
使用示例
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)
2.2.6 LRANGE
- 获取指定区间的元素
LRANGE key start stop
返回列表key
中,以偏移量start
和stop
指定的区间内的元素。
LRANGE
使用闭区间,即:start
和stop
索引位的元素都包含在取值范围内(如:LRANGE list 0 10
会返回11
元素)。当start
和stop
超出有效索引位范围时不会引起错误,如果start
大于最大下标值会返回一个空列表;如果stop
大于最大下标值,会自动设置为最后一位。
复杂度、返回值:
- 时间复杂度:
O(S+N)
,S
为偏移量start
,N
为指定区间内元素的数量 - 返回值:一个包含指定元素的列表
使用示例
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"
2.3 列表修改/元素移动
Redis 提供了LSET
命令,用于修改列表指定位置元素的值。除了这个命令外,我们还可以使用RPOPLPUSH
将元素从一个列表移动到另一个列表。
2.3.1 LSET
- 设置指定位元素
LSET key index value
设置列表key
中下标为index
的元素值为value
。当index
超出范围,或对一个空列表进行设置时,会返回一个错误。
复杂度、返回值:
- 时间复杂度:为头/尾元素进行操作时为
O(1)
;其它情况为O(N)
,N
列表长度 - 返回值:操作成功返回
OK
,否则返回错误信息。
使用示例
# 对空列表进行 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
2.3.2 RPOPLPUSH
- 弹出尾元素,将弹出元素插入另一列表的开头
RPOPLPUSH source destination
RPOPLPUSH
命令包含以下两个原子操作:
- 将列表
source
的尾元素弹出,并返回给客户端。 - 将
source
弹出的元素,作为destination
列表的头元素插入。
如果source
不存在,返回nil
。如果source
和destination
相同,就会把尾元素移动至开头,这叫做列表的旋转(rotation
)操作。
复杂度、返回值:
- 时间复杂度:
O(1)
- 返回值:被弹出的元素。
使用示例
# 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"
2.3.3 BRPOPLPUSH
- 阻塞并弹出尾元素,将弹出元素插入另一列表的开头
BRPOPLPUSH source destination timeout
BRPOPLPUSH
是RPOPLPUSH
命令的阻塞版本。当指定的源列表source
不为空时,其表现和RPOPLPUSH
一样。当source
为空时,连接将被BRPOP
命令阻塞,直到等待超时或有可弹出元素为止。
timeout
参数表示阻塞的时长,如果为0
表示可以无限期延长阻塞。
复杂度、返回值:
- 时间复杂度:
O(1)
- 返回值:如果指定时间内没有任何元素弹出,返回一个
nil
。 否则,返回一个含有两个元素的列表,其中:第一个元素是被弹出元素所属的key
,第二个元素是被弹出元素的值
。
使用示例
# 非空列表 redis> BRPOPLPUSH msg reciver 500 "hello moto" # 弹出元素的值 (3.38s) # 等待时长 redis> LLEN reciver (integer) 1 redis> LRANGE reciver 0 0 1) "hello moto" # 空列表 redis> BRPOPLPUSH msg reciver 1 (nil) (1.34s)
2.4 元素删除/列表裁剪
通过LREM
命令可以移除列表中不再需要的元素。而LTRIM
可以从指定区间范围裁剪列表。
2.4.1 LREM
- 移除元素
LREM key count value
移除指定数量为count
的value
值。count
可能有以下几种情况:
count >0
: 从表头开始向表尾搜索,移除值为value
,数量为count
的元素。count < 0
: 从表尾开始向表头搜索,移除值为value
,数量为count
绝对值的元素。count = 0
: 移除表中所值为value
的元素。
复杂度、返回值:
- 时间复杂度:
O(N)
,N
列表长度 - 返回值:被移除元素的数量。
key
不存在时,返回0
。
使用示例
# 创建一个列表,内容排列是 # 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" # 移除从表头到表尾,最先发现的两个 morning redis> LREM greet 2 morning (integer) 2 redis> LLEN greet # 还剩 3 个元素 (integer) 3 redis> LRANGE greet 0 2 1) "hello" 2) "hello" 3) "morning" # 移除从表尾到表头,第一个 morning redis> LREM greet -1 morning (integer) 1 # 剩下两个元素 redis> LLEN greet (integer) 2 redis> LRANGE greet 0 1 1) "hello" 2) "hello" # 移除表中所有 hello redis> LREM greet 0 hello (integer) 2 redis> LLEN greet (integer) 0
2.4.2 LTRIM
- 列表裁剪
LTRIM key start stop
保留列表key
中,偏移量start
和stop
指定的区间内的元素,裁剪其余元素。
LTRIM
使用闭区间,即:start
和stop
索引位的元素都包含在取值范围内(如:LTRIM list 0 10
结果是一个包含11
元素的列表)。
复杂度、返回值:
- 时间复杂度:
O(N)
,N
移除元素的数量 - 返回值:操作成功返回
OK
,否则返回错误信息。
使用示例
# 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)