数据库
PythonJava前端运维
Redis
Redis
  • Redis 简介
  • 安装和连接
  • Redis 键(key)
  • Redis 数据类型
    • 基本数据类型
      • String
      • Lists
      • Sets
      • zset
      • Hashs
    • 特殊类型
      • Bitmaps
      • HyperLogLog
      • Geospatial
    • Sorted sets
    • Streams
    • Geospatial
    • Bitfields
  • Redis 配置
  • Redis CLI
  • Redis 持久化
  • Redis 事务
  • Redis 管道
  • Redis 发布订阅
  • Redis 脚本
  • Redis 数据备份与恢复
  • Redis 缓存问题
  • Redis 运维监控
由 GitBook 提供支持
在本页
  • 介绍
  • 常用命令
  • 应用场景

这有帮助吗?

  1. Redis 数据类型
  2. 基本数据类型

Lists

上一页String下一页Sets

最后更新于9个月前

这有帮助吗?

介绍

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 命令从列表尾部获取消息。

# 生产者
LPUSH message-queue "message1"

# 消费者
RPOP message-queue

在生产者往 List 中写入数据时,List 并不会主动通知消费者,消费者需要主动轮询 List 列表,检查是否有新的消息。这就会导致消费者需要不断地轮询 List 列表,这会导致消费者程序的 CPU 一直消耗在执行 RPOP 命令上,带来不必要的性能开销。

为了解决这个问题,可以使用阻塞式的命令 BLPOP 或 BRPOP,这两个命令会在列表为空时阻塞,直到列表中有新的元素时才会返回。

# 生产者
LPUSH message-queue "message1"

# 消费者
BRPOP message-queue 0

如何保证消息可靠性

当消费者程序从 List 列表中读取一条消息后,List 就不会再保存这条消息了,如果消费者程序在处理消息的过程中出现异常,消息就会丢失。为了保证消息不丢失,List 类型提供了 BRPOPLPUSH 命令,该命令可以将消息从一个列表移动到另一个列表,这样即使消费者程序在处理消息的过程中出现异常,消息也不会丢失。

# 生产者
LPUSH message-queue "message1"

# 消费者, 将消息从 message-queue 移动到 processing-queue
BRPOPLPUSH message-queue processing-queue 0

在消费者程序处理完消息后,可以通过 LREM 命令将消息从 processing-queue 列表中移除。

# 消费者, 处理消息后将消息从 processing-queue 移除
LREM processing-queue 0 "message1"

如何处理消息重复消费

在消息队列中,消息可能会被重复消费,为了避免消息重复消费,可以在消费者程序处理消息之前,先检查消息是否已经被处理过。例如,可以为每条消息添加一个唯一的 ID,然后在消费者程序处理消息之前,先检查消息的 ID 是否已经处理过。

# 生产者
LPUSH message-queue "10000111:stock:1001"

# 消费者
BRPOPLPUSH message-queue processing-queue 0

但是 List 并不会为每个消息声称一个唯一的 ID,因此需要在消息中添加一个唯一的 ID,例如 UUID,然后在消费者程序处理消息之前,先检查消息的 ID 是否已经处理过。

例如,下方命令就把一条全局 ID 为 10000111 、库存量为 1001 的消息插入到 message-queue 队列中。

LPUSH message-queue "10000111:stock:1001"

List 作为消息队列的优点是实现简单,易于理解,但是在高并发场景下,List 的性能可能会成为瓶颈,因为 List 是一个全局的数据结构,所有的生产者和消费者都需要访问同一个 List 列表,这会导致 List 列表的读写压力较大。同时,List 不支持多个消费者消费同一条消息,因为一旦消息被一个消费者消费,这条消息就会从 List 列表中移除,其他消费者就无法再次消费这条消息。

Drawing