Lists
最后更新于
这有帮助吗?
List 列表是简单的字符串列表,按照插入的顺序排序,可以从头部或尾部向 List 列表添加元素。
List 列表的最大长度为 2^32-1
,即每个列表支持超过 40 亿个元素。
List 列表的底层实现是一个双向链表,因此在头部和尾部添加元素的时间复杂度为 O(1)。
如果列表的元素个数小于 512(默认值,可通过 list-max-ziplist-entries
配置项修改),并且列表中的每个元素的长度都小于 64 字节(默认值,可通过 list-max-ziplist-value
配置项修改),列表会使用压缩列表作为底层实现。
如果列表的元素个数大于 512,或者列表中的某个元素的长度大于 64 字节,列表会使用双向链表作为底层实现。
在 Redis 3.2 之后,Redis 为 List 列表添加了快速列表(quicklist)的实现,快速列表是一种新的列表实现方式,它同时兼具压缩列表和双向链表的优点,可以在列表元素个数较少时使用压缩列表,元素个数较多时使用双向链表,从而兼顾了两者的优点。
LPUSH key value [value ...]
: 将一个或多个值插入到列表头部
RPUSH key value [value ...]
: 将一个或多个值插入到列表尾部
LPOP key
: 移除并返回列表的第一个元素
RPOP key
: 移除并返回列表的最后一个元素
LINDEX key index
: 通过索引获取列表中的元素
LLEN key
: 获取列表的长度
LRANGE key start stop
: 获取列表指定范围内的元素
LSET key index value
: 通过索引设置列表元素的值
LTRIM key start stop
: 对列表进行修剪
LINSERT key BEFORE|AFTER pivot value
: 在列表中指定元素的前或后插入元素
LREM key count value
: 移除列表元素
RPOPLPUSH source destination
: 移除列表的最后一个元素,并将该元素添加到另一个列表并返回
BRPOP key [key ...] timeout
: 移除并获取列表最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
BLPOP key [key ...] timeout
: 移除并获取列表第一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
RPOPLPUSH source destination
: 移除列表的最后一个元素,并将该元素添加到另一个列表并返回
BRPOPLPUSH source destination timeout
: 移除列表的最后一个元素,并将该元素添加到另一个列表并返回,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
队列:先进先出
微信公众号订阅:例如当发布者发了一条新的微信公众号文章时,订阅者会收到一条消息,当发布者发布了一条新的微信公众号文章时,订阅者会收到一条消息。
消息队列
Redis 的 List 和 Stream 数据结构都可以用作消息队列,List 列表是最简单的消息队列实现方式,Stream 数据结构是 Redis 5.0 新增的数据结构,提供了更多的功能,例如消息的消费确认、消息的消费者组等。
List 列表可以用作消息队列,通过 LPUSH
和 RPOP
命令实现消息的生产和消费。
生产者通过 LPUSH
命令将消息插入到列表头部,消费者通过 RPOP
命令从列表尾部获取消息。
在生产者往 List 中写入数据时,List 并不会主动通知消费者,消费者需要主动轮询 List 列表,检查是否有新的消息。这就会导致消费者需要不断地轮询 List 列表,这会导致消费者程序的 CPU 一直消耗在执行 RPOP
命令上,带来不必要的性能开销。
为了解决这个问题,可以使用阻塞式的命令 BLPOP
或 BRPOP
,这两个命令会在列表为空时阻塞,直到列表中有新的元素时才会返回。
当消费者程序从 List 列表中读取一条消息后,List 就不会再保存这条消息了,如果消费者程序在处理消息的过程中出现异常,消息就会丢失。为了保证消息不丢失,List 类型提供了 BRPOPLPUSH
命令,该命令可以将消息从一个列表移动到另一个列表,这样即使消费者程序在处理消息的过程中出现异常,消息也不会丢失。
在消费者程序处理完消息后,可以通过 LREM
命令将消息从 processing-queue 列表中移除。
在消息队列中,消息可能会被重复消费,为了避免消息重复消费,可以在消费者程序处理消息之前,先检查消息是否已经被处理过。例如,可以为每条消息添加一个唯一的 ID,然后在消费者程序处理消息之前,先检查消息的 ID 是否已经处理过。
但是 List 并不会为每个消息声称一个唯一的 ID,因此需要在消息中添加一个唯一的 ID,例如 UUID,然后在消费者程序处理消息之前,先检查消息的 ID 是否已经处理过。
例如,下方命令就把一条全局 ID 为 10000111
、库存量为 1001
的消息插入到 message-queue
队列中。
List 作为消息队列的优点是实现简单,易于理解,但是在高并发场景下,List 的性能可能会成为瓶颈,因为 List 是一个全局的数据结构,所有的生产者和消费者都需要访问同一个 List 列表,这会导致 List 列表的读写压力较大。同时,List 不支持多个消费者消费同一条消息,因为一旦消息被一个消费者消费,这条消息就会从 List 列表中移除,其他消费者就无法再次消费这条消息。