目录
- 一、简介
- 二、相关命令
-
- [2.1 lpush 和 lrange](#2.1 lpush 和 lrange)
- [2.2 lpushx](#2.2 lpushx)
- [2.3 rpush 和 rpushx](#2.3 rpush 和 rpushx)
- [2.4 lpop 和 rpop](#2.4 lpop 和 rpop)
- [2.5 lindex 和 linsert](#2.5 lindex 和 linsert)
- [2.6 llen](#2.6 llen)
- [2.7 lrem](#2.7 lrem)
- [2.8 ltrim 和 lset](#2.8 ltrim 和 lset)
- [2.9 blpop 和 brpop](#2.9 blpop 和 brpop)
- [2.10 小结](#2.10 小结)
- 三、内部编码
- 四、应用场景
-
- [4.1 消息队列](#4.1 消息队列)
- [4.2 分频道的消息队列](#4.2 分频道的消息队列)

一、简介
列表List相当于 数组 或顺序表。

列表中的每个字符串称为元素(element),⼀个列表最多可以存储 个元素。在 Redis 中,可以对列表两端插⼊(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等。
特点:
- 列表中的元素是有序的(列表中元素顺序发生改变,就是一个新列表了),这意味着可以通过索引下标获取某个元素或者某个范围的元素列表。
- 、列表中的元素是允许重复的。
- 列表中元素从左到右下标 0 到 n-1 ,也可以从右到左 -1 到 -n 作为元素下标。
二、相关命令
2.1 lpush 和 lrange
lpush将⼀个或者多个元素从左侧放⼊(头插)到 List 中。
语法:
lpush key element [element ...]
时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数.
返回值:插⼊后 List 的⻓度。

lrange获取从 start 到 stop 区间的所有元素,左闭右闭,越界的下标不返回元素。
语法:lrange key start stop
时间复杂度:O(N)
返回值:指定区间的元素。

2.2 lpushx
lpushx在 key 存在时,将⼀个或者多个元素从左侧放⼊(头插)到 List 中。不存在,直接返回
语法:
lpushx key element [element ...]
命令有效版本:2.0.0之后
时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数.
返回值:插⼊后 list 的⻓度。

2.3 rpush 和 rpushx
rpush 将⼀个或者多个元素从右侧放⼊(尾插)到 list 中。
语法:
rpush key element [element ...]
时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数.
返回值:插⼊后 list 的⻓度。

rpushx在 key 存在时,将⼀个或者多个元素从左侧放⼊(头插)到 List 中。不存在,直接返回
语法:
rpushxkey element [element ...]
命令有效版本:2.0.0之后
时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数.
返回值:插⼊后 list 的⻓度。

2.4 lpop 和 rpop
lpop从 list 左侧取出元素(即头删)。
语法:
lpop key [count]
时间复杂度:O(1)
返回值:取出的元素或者 nil。

rpop从 list 右侧取出元素(即尾删)。
语法:
rpop key [count]
时间复杂度:O(1)
返回值:取出的元素或者 nil。

从Redis 6.2 后的版本,才新增了一个count参数。描述这次头/尾删,删几个元素。
2.5 lindex 和 linsert
lindex获取从左数第 index 位置的元素。
语法:
lindex key index
时间复杂度:O(N)
返回值:取出的元素或者 nil。

linsert在特定位置插⼊元素。
语法:
linsert key <BEFORE | AFTER> pivot element
- before: 插在 pivot 左边
- after: 插在 pivot 右边
- pivot :元素值,从左往右找到的第一个
命令有效版本:2.2.2 之后
时间复杂度:O(N)
返回值:插⼊后的 list ⻓度。

2.6 llen
llen 获取 list ⻓度。
语法:
llen key
时间复杂度:O(1)
返回值:list 的⻓度。

2.7 lrem
lrem 删除一个或多个元素
语法:
lrem key count element
- count : 要删除的元素个数,count大于0,从左往右找删除count个;count小于0,从右往左找删除-count个;count为0,删除所有element元素。
- element:要删除的元素值
时间复杂度:O(N+M) , N是列表List长度,M是要删除的个数。
返回值:成功删除的元素个数。

2.8 ltrim 和 lset
ltrim 保留区间 start 和 stop 区间(左闭右闭)的元素,其余元素全部删除。
语法:
ltrim key start stop
时间复杂度:O(N) , N是保留元素个数。
返回值:OK

lset 根据下标修改元素
语法
lset key index element
时间复杂度:O(N)
返回值:成功修改OK,下标越界报错(error) ERR index out of range

2.9 blpop 和 brpop
blpop 和 brpop 是 lpop 和 rpop 的阻塞版本,和对应⾮阻塞版本的作⽤基本⼀致,除了:
- 在列表中有元素的情况下,阻塞和⾮阻塞表现是⼀致的。但如果列表中没有元素,⾮阻塞版本会理解返回 nil,但阻塞版本会根据 timeout,阻塞⼀段时间(可以设置等待时间 ),期间 Redis 可以执⾏其他命令(阻塞期间不会对Redis使用产生影响),但要求执⾏该命令的客⼾端会表现为阻塞状态。
- 命令中如果设置了多个键,那么会从左向右进⾏遍历键,⼀旦有⼀个键对应的列表中可以弹出元素,命令⽴即返回。
- 如果多个客⼾端同时多⼀个键执⾏ pop,则最先执⾏命令的客⼾端会得到弹出的元素。



blpop lpop 头删 的阻塞版本。
语法:
blpop key [key ...] timeout
时间复杂度:O(1)
返回值:取出的元素或者 nil。

brpop rpop 尾删 的阻塞版本。
语法:
brpop key [key ...] timeout
时间复杂度:O(1)
返回值:取出的元素或者 nil。

2.10 小结
| 操作类型 | 命令 | 执行效果 | 时间复杂度 |
|---|---|---|---|
| 添加 | rpush key value [value ...] | 尾插一个或多个元素 | O(k),k 是元素个数 |
| 添加 | lpush key value [value ...] | 头插一个或多个元素 | O(k),k 是元素个数 |
| 添加 | linsert key <before / after> pivot value | 在特定位置 pivot元素 左边或右边 插⼊元素 | O(n),n 是 pivot 距离头尾的距离 |
| 查找 | lrange key start end | 获取从 start 到 end 区间的所有元素,左闭右闭,越界的下标不返回元素 | O(s+n),s 是 start 偏移量,n 是 start 到 end 的范围 |
| 查找 | lindex key index | 获取从左数第 index 位置的元素 | O(n),n 是索引的偏移量 |
| 查找 | llen key | 获取 list ⻓度 | O(1) |
| 删除 | lpop key | 头删一个元素 | O(1) |
| 删除 | rpop key | 尾删一个元素 | O(1) |
| 删除 | lremkey count value | 删除一个或多个元素 | O(k),k 是元素个数 |
| 删除 | ltrim key start end | 保留区间 start 和 stop 区间(左闭右闭)的元素,其余元素全部删除 | O(k),k 是元素个数 |
| 修改 | lset key index value | 根据下标修改元素 | O(n),n 是索引的偏移量 |
| 阻塞操作 | blpop brpop | O(1) |
三、内部编码
Redis3.2之前,列表类型的内部编码有两种:
- ziplist(压缩列表):当列表的元素个数⼩于 list-max-ziplist-entries 配置(默认 512 个),同时列表中每个元素的⻓度都⼩于 list-max-ziplist-value 配置(默认 64 字节)时,Redis 会选⽤ ziplist 来作为列表的内部编码实现来减少内存消耗。
- linkedlist(链表):当列表类型⽆法满⾜ ziplist 的条件时,Redis 会使⽤ linkedlist 作为列表的内部实现。
在Redis3.2之后,使用quicklist代替上面两种。quicklist就是一个元素是ziplist的链表,使用 list-max-ziplist-size 配置每个压缩列表的大小。

四、应用场景
4.1 消息队列
Redis 可以使⽤ lpush + brpop 命令组合实现经典的阻塞式⽣产者-消费者模型队列,⽣产者客⼾端使⽤ lpush 从列表侧插⼊元素,多个消费者客⼾端使⽤ brpop 命令阻塞式地从队列中"争抢" 队⾸元素。通过多个客⼾端来保证消费的负载均衡和⾼可⽤性。

4.2 分频道的消息队列
Redis 同样使⽤ lpush + brpop 命令,但通过不同的键模拟频道的概念,不同的消费者可以通过 brpop 不同的键值,实现订阅不同频道的理念。
