列表的内部编码
列表类型的内部编码有两种:
- ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个)
同时所有值都小于hash-max-ziplist-value配置(默认64个字节)时,Redis会使用ziplist作为哈希的内部实现。 - linkedlist(链表):当列表类型无法满足ziplist的条件时,Redis会使用linkedlist作为列表的内部实现。
下面演示列表类型的内部编码,以及相应的变化:
当元素个数较少且没有大元素时,内部编码为ziplist:
127.0.0.1:6379> rpush list:2 a b c
(integer) 3
127.0.0.1:6379> object encoding list:2
"ziplist"
当元素个数超过512个,内部编码变为linkedlist:
127.0.0.1:6379>lpush setkey 1 2 3 ... 513
OK
127.0.0.1:6379> object encoding listkey
"linkedlist"
当某个元素超过64个字节,内部编码也会变为linkedlist:
127.0.0.1:6379> rpush list:1 a b "我不再说话,不再思索,但无尽的爱从灵魂中升起,我将远行,走得很远,如同一个吉普塞人,穿过大自然——幸福得如有一位女子同行。"
(integer) 6
127.0.0.1:6379> object encoding list:1
"linkedlist"
只能升级,不能自动变回ziplist类型
列表结构
结构依然还是key-value的结构,key还是字符串类型,value是一个有序的队列。
- 可以进行lpush、lpop、rpush、rpop。
- 可以llen(计算列表长度)、lrem 1 b(删除指点元素)、lrange(获取子列表)、lindex 5 (按索引获取列表指定元素)。
特点
- 有序
- 可以重复
- 左右两边插入弹出
列表类型的相关命令:
增
rpush、lpush
- rpush key value1 value2 … valueN:从列表右端插入值(1 ~ N个),时间复杂度O(1 ~ n)。
- lpush key value1 value2 … valueN:从列表左端插入值(1 ~ N个),时间复杂度O(1 ~ n)。
# 加入单个元素
redis> lpush languages python
(integer) 1
# 加入重复元素
redis> lpush languages python
(integer) 2
redis> lpush languages 0 -1 # 列表允许重复元素
1) "python"
2) "python"
# 加入多个元素
redis> lpush mylist a b c
(integer) 3
redis> lpush mylist 0 -1
1) "c"
2) "b"
3) "a"
linsert
- linsert key before|after value newValue:在list指定的 value 之前|后插入 newValue。
- 当 value 不存在于列表 key 时,不执行任何操作。
- 当 key 不存在时, key 被视为空列表,不执行任何操作。
- 如果 key 不是列表类型,返回一个错误。
- 时间复杂度O(n) ,N 为寻找 value 过程中经过的元素数量。
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、rpop
- lpop key:从列表左端弹出一个item,时间复杂度O(1)。
- rpop key:从列表右端弹出一个item,时间复杂度O(1)。
redis> llen course
(integer) 0
redis> rpush course algorithm001
(integer) 1
redis> rpush course c++101
(integer) 2
redis> lpop course # 移除头元素
"algorithm001"
lrem
- lrem key count value:根据count值,从列表中删除所有value相等的项。
- count>0,从左到右,删除最多count个value相等的项。
- count<0,从右到左,删除最多Math.abs(count)(绝对值)个value相等的向项。
- count=0,删除所有value相等的项。
- 时间复杂度O(n)。
# 先创建一个表,内容排列是
# 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
- ltrim key start end:按照索引范围修剪列表,时间复杂度O(n),n为被移除的元素的数量。
# 情况 1: 常见情况, start 和 stop 都在列表的索引范围之内
redis> lrange alpha 0 -1 # alpha 是一个包含 5 个字符串的列表
1) "h"
2) "e"
3) "l"
4) "l"
5) "o"
redis> ltrim alpha 1 -1 # 删除 alpha 列表索引为 0 的元素
OK
redis> lrange alpha 0 -1 # "h" 被删除了
1) "e"
2) "l"
3) "l"
4) "o"
# 情况 2: stop 比列表的最大下标还要大
redis> ltrim alpha 1 10086 # 保留 alpha 列表索引 1 至索引 10086 上的元素
OK
redis> lrange alpha 0 -1 # 只有索引 0 上的元素 "e" 被删除了,其他元素还在
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"
redis> ltrim new-alpha 123321 10086 # 执行 LTRIM
OK
redis> lrange new-alpha 0 -1 # 同样被清空
(empty list or set)
查
lrange
- lrange key start end (包含end):获取列表指定索引范围所有item。
- 时间复杂度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"
lindex
- lindex key index:获取列表指定索引的item。
- 时间复杂度O(N), N 为到达下标 index 过程中经过的元素数量。因此,对列表的头元素和尾元素执行 LINDEX 命令,复杂度为O(1)。
redis> lpush mylist "World"
(integer) 1
redis> lpush mylist "Hello"
(integer) 2
redis> lindex mylist 0
"Hello"
redis> lindex mylist -1
"World"
redis> lindex mylist 3 # index不在 mylist 的区间范围内
(nil)
llen
- llen key:获取列表长度,时间复杂度O(1)。
# 空列表
redis> llen job
(integer) 0
# 非空列表
redis> lpush job "cook food"
(integer) 1
redis> lpush job "have lunch"
(integer) 2
redis> llen job
(integer) 2
改
lset
- lset key index newValue:设置列表指定索引值为newValue。
- 对头元素或尾元素进行 LSET 操作,复杂度为 O(1)。其他情况下,为 O(N), N 为列表的长度。
# 对空列表(key 不存在)进行 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 # 列表长度为 1
(integer) 1
redis> lset list 3 'out of range'
(error) ERR index out of range
实战
- 实战-TimeLine
现在weibo的数据顺序是:weibo10129、weibo998、weibo10194、weibo10158、weibo100;
当你关注的人更新微博,lpusp将最新的weibo加入列表的最左边开头。
blpop、brpop
- blpop key timeout:blpop阻塞版本,timeout是阻塞超时时间,timeout=0为永远不阻塞,时间复杂度O(1)。
- brpop key timeout:brpop阻塞版本,timeout是阻塞超时时间,timeout=0为永远不阻塞,时间复杂度O(1)。
TIPS
- lpush + lpop = stack,实现栈的功能。
- lpush + rpop = Queue,实现队列的功能。
- lpush + ltrim = capped collection,实现由固定数量的列表的功能。
- lpush + brpop = message queue,实现消息队列的功能。