文章目录
Redis的list类及基础指令
一、list类特点
- 列表中的元素是有序的
- 列表中的元素可以重复
二、内部实现
- 3.2之前的版本
| 类型 | 解释 |
|---|---|
| ziplist 压缩链表 | ziplist 是一块连续内存,插入/删除时需要移动后续所有元素,时间复杂度为 O(n) |
| linkedlist 双向链表 | linkedlist 是由离散节点组成,插入/删除时只需修改相邻指针,时间复杂度为 O(1) |
注意 ziplist 向 linkedlist 的转换
- 单个元素的长度过大
- 元素数量超过阈值(这个阈值可以自己到参数文件里去设置)
- 3.2之后的版本
-
只有quicklist
-
Quicklist 宏观上是双向链表,但微观上每个节点内部包含的是一个 Ziplist,用来存储一批元素
-
简单的来说就是,ziplist就相当于是普通的数组,空间大小也是连续的,插入和删除也跟普通数组一样,会"动辄全身",而linedlist就是双向链表,每个节点都记录着数据本身以及指针,指针占用空间,空间分布不连续,插入和删除较快,而quicklist整合了这个两个list的有点,存储上以ziplist为单位,结构上使用linedlist,既节省了空间,也加快了插入删除的效率。
三、常用命令
1. LPUSH、RPUSH
lpush: 将⼀个或者多个元素从左侧放入(头插)到 list 中
rpush: 将⼀个或者多个元素从右侧放入(尾插)到 list 中
-
语法
LPUSH key element [element ...]
RPUSH key element [element ...] -
返回值:插入list后的长度
-
示例
redis> LPUSH mylist "world"
(integer) 1
redis> LPUSH mylist "hello"
(integer) 2
redis> LRANGE mylist 0 -1- "hello"
- "world"
redis> RPUSH mylist "world"
(integer) 1
redis> RPUSH mylist "hello"
(integer) 2
redis> LRANGE mylist 0 -1- "world"
- "hello"
2. LPUSHX、RPUSHX
在 key 存在时,将⼀个或者多个元素从左(右)侧放入(头插或尾插)到 list 中。不存在,直接返回。
-
语法
LPUSHX key element [element ...]
RPUSHX key element [element ...] -
返回值:插入后的list长度
-
示例
redis> LPUSH mylist "World"
(integer) 1
redis> LPUSHX mylist "Hello"
(integer) 2
redis> LPUSHX myotherlist "Hello"
(integer) 0
redis> LRANGE mylist 0 -1- "Hello"
- "World"
redis> LRANGE myotherlist 0 -1
(empty array)
3. LRANGE
获取从start到end区间的所有元素,左闭右闭 start, end
-
语法
LRANGE key start end -
返回值:指定区间的元素
-
示例
redis> RPUSH mylist "one"
(integer) 1
redis> RPUSH mylist "two"
(integer) 2
redis> RPUSH mylist "three"
(integer) 3
redis> LRANGE mylist 0 0- "one"
redis> LRANGE mylist -3 2 - "one"
- "two"
- "three"
redis> LRANGE mylist -100 100 - "one"
- "two"
- "three"
redis> LRANGE mylist 5 10
(empty array)
- "one"
4. LPOP、RPOP
从list左侧或者右侧取出元素(头删或尾删)
-
语法
LPOP key RPOP key -
返回值:取出的元素或者nil
-
示例
redis> RPUSH mylist "one" "two" "three" "four" "five"
(integer) 5
redis> LPOP mylist
"one"
redis> LPOP mylist
"two"
redis> LPOP mylist
"three"
redis> LRANGE mylist 0 -1- "four"
5. LINSERT
在特定的位置前面或者后面插入元素
-
语法:
LINSERT key <BEFORE | AFTER> pivot element
-
返回值:插入后list的长度
-
示例
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- "Hello"
- "There"
- "World"
6. LLEN
获取lsit长度
-
语法
LLEN key
-
返回值:返回list长度
三、阻塞版本的命令
-
Redis中的list可以当做简单的消息队列使用,通过BLPOP, BRPOP来实现消费者操作
-
BLPOP、BRPOP
-
语法
BLPOP key [key ...] timeout
BRPOP key [key ...] timeout -
返回值:取出的元素或者nil
-
示例
redis> EXISTS list1 list2
(integer) 0
redis> RPUSH list1 a b c
(integer) 3
redis> BLPOP list1 list2 0- "list1"
- "a"
-
EXISTS list1 list2(返回 0):检查list1和list2这两个键是否存在。返回 0 表示这两个列表目前都不存在。 -
RPUSH list1 a b c(返回 3):向list1列表的右端依次推入 "a"、"b"、"c" 三个元素。返回 3 表示列表当前长度为 3。
BLPOP list1 list2 0
(返回 "list1" 和 "a"):这是 LPOP 的阻塞版本,用于从左侧弹出元素。
-
list1 list2:表示同时监听这两个键,Redis 会从左向右遍历,哪个列表先有元素就从哪个弹出。 -
0:表示超时时间设为 0,即永久阻塞等待,直到有元素被弹出为止。 -
返回结果是一个二元组:
"list1"表示元素是从这个 key 弹出的,"a"表示弹出的具体元素值。因为list1中有元素且排在list2前面,所以直接弹出了list1的头部元素 "a"。 -
小结
- 空列表处理差异:当列表有元素时,两者表现一致;若列表为空,非阻塞版本立即返回 nil,而阻塞版本会等待指定的 timeout 时间,期间 Redis 可处理其他命令,但客户端处于阻塞状态。
- 多键遍历机制:当命令包含多个键时,Redis 会从左向右依次遍历,一旦遇到某个键对应的列表中有元素可弹出,命令立即返回。
- 多客户端竞争规则:当多个客户端同时对同一个键执行 pop 操作时,遵循"先到先得"原则,最先执行命令的客户端会成功获取弹出的元素。