介绍

List(列表)是字符串列表,按照插入顺序排序,可以从头部或尾部向 List 添加元素

列表的最大长度为 2^32 - 1(40 多亿)

内部实现

List 类型的底层数据结构是由双向链表压缩列表实现的:

  • 如果列表的元素个数小于 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 实现栈和队列的功能

消息队列

消息队列要满足三个需求:

  1. 消息保序
  2. 处理重复的消息
  3. 保证消息可靠性

Redis 的 List 和 Stream 两种数据类型,都可以满足这三个需求,这里介绍 List 实现消息队列

消息保序

List 本身就是有序的线性结构,可以使用 LPUSH + RPOP (或者反过来:RPUSH + LPOP)实现消息队列

读取时,可以使用 BRPOPBLPOP 命令阻塞式读取,而不是无限循环调用 RPOP

处理重复的消息

消费者要实现重复消息的判断,需要 2 个要求:

  1. 每个消息都有一个全局的 ID
  2. 消费者要记录所有已处理过的消息的 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 同样能够满足消息队列的三大需求,而且它还支持「消费组」形式的消息读取