Redis-List
悲观者从机会中看到困难。乐观者从困难中看到机会。
——温斯顿·丘吉尔
list列表是简单的字符串列表,按照插入顺序排序,可以从头部或尾部向list列表添加元素。
列表的最大长度为2^32-1,也即每个列表支持超过40亿个元素
内部实现
List 类型的底层数据结构是由双向链表或压缩列表实现的:
- 如果列表的元素个数小于 512 个(默认值,可由 list-max-ziplist-entries 配置),列表每个元素的值都小于 64 字节(默认值,可由 1ist-max-ziplist-value配置),Redis 会使用压缩列表作为List 类型的底层数据结构;
- 如果列表的元素不满足上面的条件,Redis 会使用双向链表作为 List 类型的底层数据结构;
但是在 Redis 3.2 版本之后,List 数据类型底层数据结构就只由 quicklist 实现了,替代了双向链表和压缩
列表。
---应用场景
消息队列
消息队列在存取消息时,必须满足三个需求,分别是消息保存,处理重复消息和保证消息可靠性
Redis的list 和 stream 两种数据类型,就可以满足消息队列的这三个需求。我们先来了解下基于List的消息队列实现方法:
1.如何满足消息保序需求?
List本身就是按先进先出的顺序对数据进行存取的,所以,如果使用List作为消息队列保存消息的话,就已经能满足消息保序的需求了。
List可以使用LPUSH + RPOP(或者反过来,RPUSH + LPOP)命令来实现消息队列。

- 生产者使用
LPUSH key value [value...]将消息插入到队列的头部,如果key不存在则会创建一个空的队列再插入消息。 - 消费者使用
RPOP key依次读取队列的消息,先进先出
不过,在消费者读取数据时,有一个潜在的性能风险点。
在生产者往 List 中写入数据时,List 并不会主动地通知消费者有新消息写入,如果消费者想要及时处理消息,就需要在程序中不停地调用 RPOP命令(比如使用一个while(1)循环)。如果有新消息写入,RPOP命令就会返回结果,否则,RPOP命令返回空值,再继续循环。
所以,即使没有新消息写入List,消费者也要不停地调用 RPOP 命令,这就会导致消费者程序的 CPU 一直消耗在执行 RPOP 命令上,带来不必要的性能损失。
为了解决这个问题,Redis提供了 BRPOP 命令。BRPOP命令也称为阻塞式读取,客户端在没有读到队列数据时,自动阻塞,直到有新的数据写入队列,再开始读取新数据。和消费者程序自己不停地调用RPOP命令相比,这种方式能节省CPU开销。

2.如何处理重复消息?
消费者要实现重复消息的判断,需要2个方面的要求:
- 每个消息都有一个全局的ID
- 消费者要记录已经处理过的消息的ID。当收到一条消息后,消费者程序就可以对比收到的消息ID和记录已经处理过的消息ID,来判断当前收到的消息有没有经过处理。如果已经处理过,那么,消费者程序就不再进行处理了。
但是list并不会为每个消息生成ID号,所以我们需要自行为每个消息生成一个全局唯一ID,生成之后,我们在用LPUSH命令把消息插入List时,需要在消息中包含这个全局唯一ID。
例如,我们执行以下命令,就把一条全局ID为111000102、库存量为99的消息插入了消息队列:
> LPUSH mq "111000102:stock:99"
(integer) 1
3.如何保证消息的可靠性?
当消费者程序从list读取一条消息后,list就不会再留存这条消息了。所以,如果消费者程序在处理消息过程中出现了故障或宕机,就会导致消息没有处理完成,那么,消费者程序再次启动后,就没法再次从list中读取消息了。
为了留存消息,list类型提供了BRPOPLPUSH命令,这个命令的作用是让消费者程序从一个list中读取消息,同时,Redis会把这个消息再插入到另一个list(可以叫做备份list)留存
这样一来,如果消费者程序读了消息但没能正常处理,等它重启后,就可以从备份List中重新读取消息并进行处理了
到这里可以知道基于List类型的消息队列,满足消息队列的三大需求(1.消息保序 2.处理重复消息 3.保证消息可靠性)
- 消息保序:LPUSH + RPOP
- 阻塞读取:使用BRPOP
- 处理重复消息:生产者自行实现全局唯一ID
- 保证消息可靠性:使用BRPOPLPUSH
4.List作为消息队列有什么缺陷?
List不支持多个消费者消费同一条消息,因为一旦消费者拉取一条消息后,这条消息就从List中删除了,无法被其他消费者再次消费。
要实现一条消息可以被多个消费者消费,那么就要将多个消费者组成一个消费组,使得多个消费者可以消费同一条消息,但是List类型并不支持消费组的实现
这就要说起Redis从5.0版本开始提供的Stream数据类型了,Stream同样能够满足消息队列的三大需求,而且它还支持‘消费组’形式的消息读取。

浙公网安备 33010602011771号