介绍
List(列表)是字符串列表,按照插入顺序排序,可以从头部或尾部向 List 添加元素
列表的最大长度为 2^32 - 1
(40 多亿)
内部实现
- 如果列表的元素个数小于 512(默认值,可由
list-max-ziplist-entries
配置),列表每个元素的值都小于 64 字节(默认值,可由list-max-ziplist-value
配置),使用压缩列表 - 否则,使用双向链表
在 Redis 3.2 版本后,List 底层数据结构就只由 quicklist 实现了,替代了双向链表和压缩列表
常用命令
# 将一个或多个值 value 插入到 key 列表的表头(最后的值在最前面)
LPUSH key value [value ...]
# 将一个或多个值 value 插入到 key 列表的表尾
RPUSH key value [value ...]
# 移除并返回 key 列表的头元素
LPOP key
# 移除并返回 key 列表的尾元素
RPOP key
# 返回列表 key 中指定区间内的元素(索引从 0 开始)
LRANGE key start stop
# 从 key 列表表头弹出一个元素,没有就阻塞 timeout 秒,如果 timeout=0 则一直阻塞
BLPOP key [key ...] timeout
# 从 key 列表表尾弹出一个元素,没有就阻塞 timeout 秒,如果 timeout=0 则一直阻塞
BRPOP key [key ...] timeout
应用场景
栈和队列
可以使用 List 实现栈和队列的功能
消息队列
消息队列要满足三个需求:
- 消息保序
- 处理重复的消息
- 保证消息可靠性
Redis 的 List 和 Stream 两种数据类型,都可以满足这三个需求,这里介绍 List 实现消息队列
消息保序
List 本身就是有序的线性结构,可以使用 LPUSH + RPOP
(或者反过来:RPUSH + LPOP
)实现消息队列
读取时,可以使用 BRPOP
或 BLPOP
命令阻塞式读取,而不是无限循环调用 RPOP
处理重复的消息
消费者要实现重复消息的判断,需要 2 个要求:
- 每个消息都有一个全局的 ID
- 消费者要记录所有已处理过的消息的 ID
List 不会为每个消息生成 ID 号,需要自行为每个消息生成一个全局唯一 ID
例如,把一条全局 ID 为 111000102、库存量为 99 的消息插入消息队列:
> LPUSH mq "111000102:stock:99"
(integer) 1
保证消息可靠性
当消费者从 List 读取一条消息后,List 就会删除此条数据。如果消费者程序在处理消息过程中出现故障或宕机,就会导致消息没有处理完成,且该消息丢失
为了留存消息,List 提供了 BRPOPLPUSH
命令,作用是当从一个 List 读取消息后,Redis 会把这个消息再插入到另一个 List(备份 List)留存
List 作为消息队列的缺陷
List 不支持多个消费者消费同一条消息,因为一旦消费者拉取一条消息后,这条消息就从 List 中删除了,无法被其它消费者再次消费
Stream 同样能够满足消息队列的三大需求,而且它还支持「消费组」形式的消息读取