图解Redis 03 | List数据类型的原理及应用场景

news/2024/10/18 8:11:02/

介绍

List是一个简单的字符串列表,按照元素的插入顺序进行排序。您可以从头部或尾部添加元素到这个列表中。

列表的最大长度为2^32 - 1,即支持多达40亿个元素。

内部实现

List 类型的底层数据结构在 Redis 中可以采用双向链表或压缩列表(ziplist),具体使用哪种结构取决于列表的元素数量和每个元素的大小:

  • 如果List中的元素数量少于 512 个(默认值,可通过配置 list-max-ziplist-entries 调整),且每个元素的值小于 64 字节(默认值,可通过配置 list-max-ziplist-value 调整),Redis 会使用压缩列表作为底层数据结构。这种结构有助于节省内存。

  • 如果List的元素数量超过上述限制或单个元素的大小超过了规定的字节数,Redis 会改用双向链表作为底层数据结构,以支持更复杂的操作。

不过从 Redis 3.2 版本开始,List 数据类型的底层数据结构被统一为 quicklist,这种结构结合了压缩列表和双向链表的优点,取代了原有的双向链表和压缩列表。

常用命令

以下是Redis List类型的一些常用命令:

#将一个或多个值从List左边插入。
LPUSH key value [value...]#将一个或多个值从List右边插入。
RPUSH key value [value...]#删除并返回List左边的元素。
LPOP key#删除并返回List右边的元素。
RPOP key# 获取List中元素的个数。
LLEN key#获取List指定范围内的元素。
LRANGE key start stop#按指定数量移除List中的元素
LREM key count value#设置List中指定索引处的元素。
LSET key index value#修剪List
LTRIM key start stop:

示例:

> lpush mq 10001:stock:9
(integer) 1
> lpush list v1
(integer) 1
> rpush list v2
(integer) 2
> rpush list v3
(integer) 3
> llen list
(integer) 3
> lrange list 0 2
1) "v1"
2) "v2"
3) "v3"
> lpop list
"v1"
> llen list
(integer) 2

应用场景

1. 消息队列

消息队列在处理和存储消息时,必须满足以下三个要求:

  • 保证消息顺序
  • 处理重复消息
  • 确保消息的可靠性。

Redis 的两种数据类型——List 和 Stream都可以用来实现消息队列的功能。

首先,我们将探讨如何使用 List 实现消息队列的功能,后面在介绍 Stream 数据类型时,我们将详细讨论 Stream 的实现方式。

1. 如何满足消息有序性

List本身是按照先进先出的顺序访问和存储数据的,所以如果使用List作为消息队列来保存消息,已经可以满足消息有序性的要求了。

List可以使用LPUSH+RPOP(或反之,RPUSH+LPOP)命令实现消息队列。

  1. 生产者:使用命令LPUSH key value[value…]将消息插入到List头部,如果key不存在,则会创建一个空List然后插入消息。
  2. 消费者:使用命令RPOP key按顺序读取List的消息,先进先出。

然而,这种方法存在一个问题:当生产者往 List 中写入数据时,Redis 不会主动通知消费者有新数据到达。

为了让消费者能够及时处理消息,通常需要在程序中不断调用 RPOP 命令,比如通过 while(1) 循环来轮询队列。如果队列中有新数据,RPOP 命令会返回该消息;如果队列为空,RPOP 命令将返回空值,消费者则会继续循环等待新的数据。

因此,即使没有新的消息写入 List,消费者也需要不断调用 RPOP 命令,这会导致消费者程序的 CPU 不断消耗在执行 RPOP 命令上,带来不必要的性能损失。

为了解决这个问题,Redis 提供了 BRPOP 命令。BRPOP 命令是一个阻塞读取命令,当客户端没有读取到队列数据时,BRPOP 会自动阻塞,直到有新数据写入队列后才会继续执行。与消费者程序自己不断调用 RPOP 命令不同,BRPOP 能有效节省 CPU 开销,因为它只在有新数据时才会返回结果。

2. 如何处理重复消息?

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

  • 每条消息都应具有一个全局唯一的 ID。
  • 消费者需要记录已经处理过的消息 ID。在收到一条新消息后,消费者程序可以通过比较消息的 ID 与已记录的 ID 来判断消息是否已经处理过,如果消息已处理过,则跳过处理。

List 本身并不为每条消息生成 ID,因此我们需要手动为每条消息生成一个全局唯一的 ID。在生成 ID 后,当我们使用 LPUSH 命令将消息插入 List 时,消息内容中需要包含这个唯一的 ID。

例如,我们可以执行以下命令,将一条全局唯一 ID 为 10001、库存数量为 9 的消息插入到消息队列中:

> LPUSH mq "10001:stock:9"
(integer) 1
3. 如何保证消息的可靠性?

当消费者程序从 List 中读取一条消息后,该消息将从 List 中移除。如果消费者在处理这条消息时发生故障或崩溃,那么这条消息就会丢失,消费者在重启后将无法再次从 List 中读取到这条消息。

为了解决这个问题,List 类型提供了 BRPOPLPUSH 命令。这个命令允许消费者程序从一个 List 中读取一条消息,同时将这条消息插入到另一个 List(可以称为备份 List)中进行保留。这样,如果消费者在处理消息时出现问题,消息将保留在备份 List 中。在消费者程序重启后,可以从备份 List 中重新读取并处理这些消息。

通过这种方式,基于 List 类型的消息队列能够满足三大需求:消息有序、处理重复消息和保证消息的可靠性。

  • 消息有序:使用 LPUSH + RPOP。
  • 阻塞读:使用 BRPOP。
  • 重复消息处理:生产者自行实现全局唯一 ID。
  • 消息可靠性:使用 BRPOPLPUSH。

List作为消息队列有什么缺陷?

不支持多个消费者消费同一条消息:一旦一个消费者拉取了某条消息,这条消息就会从 List 中删除,其他消费者将无法再读取这条消息。

为了解决这一问题,Redis 从 5.0 版本开始引入了 Stream 数据类型。Stream 不仅可以满足消息队列的三大需求,还支持以“消费者组”的形式读取消息。有关 Stream 数据类型的更多信息,我们将在后续的文章中详细介绍。


http://www.ppmy.cn/news/1538841.html

相关文章

vue3中swiper11的使用

Swiper官网 vue中使用方法 我使用的是 “vue”: “3.5.11”,swiper版本为 “swiper”: “11.1.14”, “less”: “4.2.0” 1. 属性介绍 属性名作用slidesPerView设置slider容器能够同时显示的slides数量(carousel模式)。可以设置为数字(小…

JavaWeb 19 AJAX

"我就是希望你好,就像很多人希望我好一样,特别简单,特别真挚。也不为了什么,就是希望你好" —— 24.10.13 一、什么是AJAX AJAX Asynchronous JavaScript and XML(异步的JavaScript和XML) AJAX不是新的编程语言&…

【华为HCIP实战课程十】OSPF网络DR和BDR实战讲解,网络工程师

一、DR与BDR的基础介绍 点到点同步LSA成本小 多点接入网络同步LSA成本大,需要DR/BDR 由于MA网络中,任意两台路由器都需要传递路由信息,网络中有n台路由器,则需要建立n*(n-1)/2个邻接关系。任何一台路由器的路由变化都会导致多次传递,浪费了带宽资源,DR和BDR应运而生!…

第二章 JVM规范

第二章 JVM规范 一. 章节概述1. 认识方式2. JVM概述JVM主要功能4. 虚拟机与平台关系 二. JVM规范1. 规范作用2. 规范主要内容3. Class字节码3.1 Class文件格式3.2 常量池3.3 类定义与属性 三. ASM---------------------------------------------------------------------------…

最新版IntelliJ IDEA 2024.2.3 创建SpringBoot项目(包含各种依赖的选择和功能)

创建SpringBoot项目 1 . 打开IDEA 选择新建项目 2. 基础项目创建 在顶端几个选项可以选择创建基本的java项目 填写项目名称,文件位置,选择构建工具 3. 下方选择springboot 选择构建的方式 三种方式虽然不同但是,基本功能都一致, Gradle-Groovy 是指使用 Groovy 语言编写…

第十六周:机器学习笔记

第十六周周报 摘要Abstratc一、机器学习1. Pointer Network(指针网络)2. 生成式对抗网络(Generative Adversarial Networks | GAN)——(上)2.1 Generator(生成器)2.2 Discriminator&…

Python网络爬虫技术详解

Python网络爬虫技术详解 引言 网络爬虫(Web Crawler),又称网络蜘蛛(Web Spider)或网络机器人(Web Robot),是一种按照一定规则自动抓取互联网信息的程序或脚本。它们通过遍历网页链…

SpringMVC源码-@ControllerAdvice和 @InitBinder注解源码讲解

1.ControllerAdvice修饰的类何时被加载扫描 被ControllerAdvice修饰的类是作用于全局的 initStrategies 初始化springmvc的9大组件 initStrategies:531, DispatcherServlet (org.springframework.web.servlet) onRefresh:514, DispatcherServlet (org.springframework.web.se…