Redis自学之路—高级特性(实现消息队列)(七)

目录

简介

Redis的Key和Value的数据结构组织

全局哈希表

渐进式rehash

发布和订阅

操作命令

publish 发布消息

subscribe 订阅消息

psubscribe订阅频道

unsubscribe 取消订阅一个或多个频道

punsubscribe 取消订阅一个或多个模式

查询订阅情况-查看活跃的频道

查询订阅情况-查看频道订阅数

使用场景

缺点

Redis Stream

Stream总述

常用操作命令

生产端

xadd 追加消息

xrange 获取消息列表,会自动过滤已经删除的消息

xlen 消息长度

del 删除Stream

xdel 删除指定的消息(指定ID)

消费者

单消费者

xread从Streams开头进行消费

指定从streams的消息ID开始(不包括命令中的消息id)

从尾部读取最新的一条消息

以阻塞的方式读取最新一条消息

消费组

xgroup 创建消费组 0-表示从头开始消费

xgroup 创建消费组  $表示从尾部开始消费,只接受新消息,当前Stream消息会全部忽略

xinfo查看指定Stream的详细信息

xinfo 查看消费组Group的情况

xinfo 查看Consumer信息

消息消费

xreadgroup消费组的组内消费

xreadgroup 设置阻塞等待进行消费组内消费者消费消息

xack 消息确认

消息不消息的原因

xpending 获取消费组内消费者的未处理完毕的消息

xpending 查询详细信息

xclaim用于转移未确认(pending)消息所有权

查看已消费消息的方法

Redis中几种消息队列实现总结

1.基于List的 LPUSH+BRPOP 的实现

优点

缺点

2.基于Sorted-Set的实现

优点

缺点

3.PUB/SUB,订阅/发布模式

优点

缺点

基于Stream类型的实现

消息队列问题

Stream消息太多怎么办?

消息如果忘记ACK会怎样?

PEL如何避免消息丢失?

死信问题

Stream的高可用

分区 Partition


简介

        本文章主要了解Redis的高级特性与应用场景,很多朋友们都认为Redis只是单纯的负责项目中的缓存,以及在项目中做登录功能,数据缓存功能,其实都被误导了,Redis也是支持消息队列的,支持发布和订阅、支持生产端、消费端。目前第三方消息队列所能处理的事,Redis也是可以的,只不过Redis有它自己的优点和弊端,接下来主要来了解一下Redis的消息队列。

Redis的Key和Value的数据结构组织

全局哈希表

为了实现从键到值的快速访问,Redis 使用了一个哈希表来保存所有键值对。一个哈希表,其实就是一个数组,数组的每个元素称为一个哈希桶。一个哈希表是由多个哈希桶组成的,每个哈希桶中保存了键值对数据。

哈希桶中的 entry 元素中保存了key和value指针,分别指向了实际的键和值,这样一来,即使值是一个集合,也可以通过*value指针被查找到。因为这个哈希表保存了所有的键值对,所以,把它称为全局哈希表。

哈希表的最大好处很明显,就是可以用 O(1) 的时间复杂度来快速查找到键值对:只需要计算键的哈希值,就可以知道它所对应的哈希桶位置,然后就可以访问相应的 entry 元素。

但当往 Redis 中写入大量数据后,就可能发现操作有时候会突然变慢了。这其实是因为忽略了一个潜在 的风险点,那就是哈希表的冲突问题和 rehash 可能带来的操作阻塞。

当往哈希表中写入更多数据时,哈希冲突是不可避免的问题。这里的哈希冲突,两个 key 的哈希值和哈希桶计算对应关系时,正好落在了同一个哈希桶中。

Redis 解决哈希冲突的方式,就是链式哈希。链式哈希也很容易理解,就是指同一个哈希桶中的多个元素用一个链表来保存,它们之间依次用指针连接。

当然如果这个数组一直不变,那么hash冲突会变很多,这个时候检索效率会大打折扣,所以Redis就需要把数组进行扩容(一般是扩大到原来的两倍),但是问题来了,扩容后每个hash桶的数据会分散到不同的位置,这里设计到元素的移动,必定会阻塞IO,所以这个ReHash过程会导致很多请求阻塞。

渐进式rehash

为了避免这个问题,Redis 采用了渐进式 rehash。

首先、Redis 默认使用了两个全局哈希表:哈希表 1 和哈希表 2。一开始,当你刚插入数据时,默认使用哈希表 1,此时的哈希表 2 并没有被分配空间。随着数据逐步增多,Redis 开始执行 rehash。

1、给哈希表 2 分配更大的空间,例如是当前哈希表 1 大小的两倍

2、把哈希表 1 中的数据重新映射并拷贝到哈希表 2 中

3、释放哈希表 1 的空间

在上面的第二步涉及大量的数据拷贝,如果一次性把哈希表 1 中的数据都迁移完,会造成 Redis 线程阻塞,无法服务其他请求。此时,Redis 就无法快速访问数据了。

在Redis 开始执行 rehash,Redis仍然正常处理客户端请求,但是要加入一个额外的处理:

  • 处理第1个请求时,把哈希表 1中的第1个索引位置上的所有 entries 拷贝到哈希表 2 中
  • 处理第2个请求时,把哈希表 1中的第2个索引位置上的所有 entries 拷贝到哈希表 2 中
  • 如此循环,直到把所有的索引位置的数据都拷贝到哈希表 2 中。

这样就巧妙地把一次性大量拷贝的开销,分摊到了多次处理请求的过程中,避免了耗时操作,保证了数据的快速访问。

所以这里基本上也可以确保根据key找value的操作在O(1)左右。

不过这里要注意,如果Redis中有海量的key值的话,这个Rehash过程会很长很长,虽然采用渐进式Rehash,但在Rehash的过程中还是会导致请求有不小的卡顿。并且像一些统计命令也会非常卡顿:比如keys

按照Redis的配置每个实例能存储的最大的key的数量为2的32次方,即2.5亿,但是尽量把key的数量控制在千万以下,这样就可以避免Rehash导致的卡顿问题,如果数量确实比较多,建议采用分区hash存储。

发布和订阅

Redis提供了基于“发布/订阅(pub/sub)”模式的消息机制,此模式下,消息发布者和订阅者不进行直接的通信,发布者客户端向指定的频道(channel)发布消息,订阅该频道的每个客户端都可以收到该消息.

操作命令

Redis主要提供了发布消息、订阅频道、取消订阅以及按照模式订阅和取消订阅等命令。

publish 发布消息

publish channel message

返回值是接收到信息的订阅者数量,如果是0说明没有订阅者,这条消息就丢了(再启动订阅者也不会收到)。

subscribe 订阅消息

subscribe channel [channel ...]

订阅者可以订阅一个或多个频道,如果此时另一个客户端发布一条消息,当前订阅者客户端会收到消息。

客户端在执行订阅命令之后进入了订阅状态(类似于监听),

只能接收subscribepsubscribe,unsubscribepunsubscribe的四个命令。

psubscribe订阅频道

PSUBSCRIBE pattern [pattern ...]

通过模式订阅频道。模式是一个类似正则表达式的概念,允许客户端订阅匹配特定模式的所有频道。

示例:如果客户端执行 PSUBSCRIBE news.* 命令,它将订阅所有以 “news.” 开头的频道,并接收这些频道上发布的所有消息。

unsubscribe 取消订阅一个或多个频道

UNSUBSCRIBE channel [channel ...]

取消订阅一个或多个频道。

示例:如果客户端之前订阅了 “news.it” 频道,并执行 UNSUBSCRIBE news.it 命令,它将停止接收该频道上发布的消息。

punsubscribe 取消订阅一个或多个模式

UNSUBSCRIBE channel [channel ...]

取消订阅一个或多个模式。

示例:如果客户端之前订阅了 “news.it” 频道,并执行 UNSUBSCRIBE news.it 命令,它将停止接收该频道上发布的消息。

查询订阅情况-查看活跃的频道

pubsub channels [pattern]

Pubsub 命令用于查看订阅与发布系统状态,包括活跃的频道(是指当前频道至少有一个订阅者),其中[pattern]是可以指定具体的模式,类似于通配符。

查询订阅情况-查看频道订阅数

pubsub numsub channel

使用场景

        需要消息解耦又并不关注消息可靠性的地方都可以使用发布订阅模式。

缺点

  • 会出现消息丢失,无法找回情况
  • 无法实现消息堆积和回溯

        例如:PubSub 的生产者传递过来一个消息,Redis会直接找到相应的消费者传递过去。如果一个消费者都没有,那么消息直接丢弃。如果开始有三个消费者,一个消费者突然挂掉了,生产者会继续发送消息,另外两个消费者可以持续收到消息。但是挂掉的消费者重新连上的时候,这断连期间生产者发送的消息,对于这个消费者来说就是彻底丢失了。

所以和很多专业的消息队列系统(例如Kafka、RocketMQ)相比,Redis 的发布订阅很粗糙,但胜在足够简单,如果当前场景可以容忍的这些缺点,也不失为一个不错的选择。

正是因为 PubSub 有这些缺点,它的应用场景其实是非常狭窄的。

从Redis5.0 新增了 Stream 数据结构。

Redis Stream

        Redis5.0最大的新特性就是多出了一个数据结构Stream,它是一个新的强大的支持多播的可持久化的消息队列,Redis的作者声明Redis Stream借鉴了Kafka的设计。

Stream总述

Redis Stream 的结构如上图所示,每一个Stream都有一个消息链表,将所有加入的消息都串起来,每个消息都有一个唯一的 ID 和对应的内容。消息是持久化的,Redis 重启后,内容还在。

具体操作:

1.每个Stream都有唯一的名称,它就是Redis的key,首次使用xadd指令追加消息时自动创建。

  •    消息ID的形式是timestampInMillis-sequence。
    • 例如1537856881582-3,它表示当前的消息在毫米时间戳1537856881582时产生,并且是该毫秒内产生的第5条消息。
  •    消息ID可以由服务器自动生成(*代表默认自动),也可以由客户端自己指定,但是形式必须是整数-整数,而且必须是后面加入的消息的ID要大于前面的消息ID。
  •    消息内容就是键值对,跟hash结构的键值对一样。

2.每个Stream都可以挂多个消费组,每个消费组会有个游标last_delivered_id在Stream数组之上往前移动,表示当前消费组已经消费到哪条消息了。

每个消费组都有一个Stream内唯一的名称,消费组不会自动创建,它需要单独的指令xgroup create进行创建,需要指定从Stream的某个消息ID开始消费,这个ID用来初始化last_delivered_id变量。

3.每个消费组(Consumer Group)的状态都是独立的,相互不受影响。同一份Stream内部的消息会被每个消费组都消费到。

4.同一个消费组(Consumer Group)可以挂接多个消费者(Consumer),这些消费者之间是竞争关系,任意一个消费者读取了消息都会使游标last_delivered_id往前移动,每个消费者有一个组内唯一名称。

        多个消费者可以达到流量分摊的目的,为大业务流量的场景做负载和分流。

5.消费者(Consumer)内部会有个状态变量pending_ids,它记录了当前已经被客户端读取,但是还没有ack的消息。

        如果客户端没有ack,这个变量里面的消息ID会越来越多,一旦某个消息被ack,它就开始减少。这个pending_ids变量在Redis官方被称为PEL,也就是Pending Entries List,这是一个核心的数据结构,它用来确保客户端至少消费了消息一次,而不会在网络传输的中途丢失了没处理。

常用操作命令

生产端

xadd 追加消息

xadd第一次对于一个stream使用可以生成一个stream的结构

xadd key [NOMKSTREAM] [MAXLEN|MINID [=|~] threshold [LIMIT count]] *|ID field value [field value ...]

  •  key:流的键名
  • ID:新条目的ID。如果指定为*,Redis会自动生成一个唯一的ID。
  • field value:一个或多个键值对,表示添加到流中的条目数据。
  • [NOMKSTREAM]:如果流不存在

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/888399.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java-09 深入浅出 MyBatis - 注解开发 注解映射 基本介绍 与 一对一模型

点一下关注吧!!!非常感谢!!持续更新!!! 大数据篇正在更新!https://blog.csdn.net/w776341482/category_12713819.html 目前已经更新到了: MyBatis&#xff…

【k8s】kubelet 的相关证书

在 Kubernetes 集群中,kubelet 使用的证书通常存放在节点上的特定目录。这些证书用于 kubelet 与 API 服务器之间的安全通信。具体的位置可能会根据你的 Kubernetes 安装方式和配置有所不同,下图是我自己环境【通过 kubeadm 安装的集群】中的kubelet的证…

3.建立本地仓库及常用命令

1.建立本地仓库 要使用Git对我们的代码进行版本控制,首先需要获得本地仓库 1)在电脑的任意位置创建一个空目录,作为我们的本地Git仓库 2)进入这个目录,右键点击Git Bash 窗口 3)执行命令git init 4) 如果创…

Narya.ai正在寻找iOS工程师!#Mixlab内推

如果你对AI技术和iOS开发充满热情,这里有一个绝佳的机会加入一家专注于AI应用创新的初创公司。Narya.ai正在招聘iOS工程师,帮助他们开发下一代效率工具,旨在提升用户的日常生活效率与幸福感。 关于Narya.ai: 专注于AI应用层创新&a…

AI开发:生成式对抗网络入门 模型训练和图像生成 -Python 机器学习

阶段1:GAN是个啥? 生成式对抗网络(Generative Adversarial Networks, GAN),名字听着就有点“对抗”的意思,没错!它其实是两个神经网络互相斗智斗勇的游戏: 生成器(Gene…

039集——渐变色之:CAD中画彩虹()(CAD—C#二次开发入门)

(来左边儿 跟我一起画个龙,在你右边儿 画一道彩虹 ~~~~~~~~~~~ ) 效果如下: 以下展示部分颜色源码: namespace AcTools {public class Class1{public Wform.Timer timer;//定时器需建在类下面public s…

C++知识整理day3类与对象(下)——赋值运算符重载、取地址重载、列表初始化、友元、匿名对象、static

文章目录 1.赋值运算符重载1.1 运算符重载1.2 赋值运算符重载 2.取地址重载2.1 const成员函数2.2 取地址运算符重载 3.类与对象的补充3.1 再探构造函数---初始化列表3.2 类型转换3.3 static成员3.4 友元3.5 内部类3.6 匿名对象3.7 对象拷贝时的编译器优化 1.赋值运算符重载 赋…

web vue 滑动选择 n宫格选中 九宫格选中

页面动态布局经常性要交给客户来操作,他们按时他们的习惯在同一个屏幕内显示若干个子视图,尤其是在医学影像领域对于影像的同屏显示目视对比显的更为重要。 来看看如下的用户体验: 设计为最多支持5行6列页面展示后,右侧的布局则动…

解决idea使用maven打包时无法将本地lib库文件和resource目录中的资源文件打包进jar文件的问题!!!

一、问题复现 1)项目结构如下 我们看到项目中手动添加了本地lib资源,同时bootspring的配置文件和mapper文件也放在了resouces目录中。 2)上述结构的项目在使用maven打包时,最终生成的jar文件中将不包含lib库文件,甚…

【短视频矩阵系统==saas技术开发】

在数字媒体领域,短视频的崛起已不可忽视。对于商业实体而言,掌握如何通过短视频平台有效吸引潜在客户并提高转化率,已成为一项关键课题。本文旨在深入剖析短视频矩阵系统的构成与作用机制,以期为企业提供一套系统化的策略&#xf…

C_字符串的一些函数

1.字符串输入函数 scanf("%s",数组名)&#xff1b; gets(数组名)&#xff1b; 区别&#xff1a; scanf(“%s”,数组名); 把空格识别为输入结束 #include <stdio.h>int main() {char a[10];printf("输入&#xff1a;");scanf("%s",a)…

JavaScript实现tab栏切换

JavaScript实现tab栏切换 代码功能概述 这段代码实现了一个简单的选项卡&#xff08;Tab&#xff09;切换功能。它通过操作 HTML 元素的类名&#xff08;class&#xff09;来控制哪些选项卡&#xff08;Tab&#xff09;和对应的内容板块显示&#xff0c;哪些隐藏。基本思路是先…

《网络聊天室项目:全面分析测试报告》

目录 一、项目介绍二、项目功能三、测试计划1. 编写测试用例2. 实际执行测试的部分操作步骤3. 自动化测试 四、项目bug&#xff08;1&#xff09;bug描述&#xff08;2&#xff09;bug描述 五、项目总结 一、项目介绍 网络聊天室项目实现了一个网络交流平台&#xff0c;用户在w…

大数据新视界 -- 大数据大厂之 Hive 数据压缩:优化存储与传输的关键(上)(19/ 30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

软通动力携子公司鸿湖万联、软通教育助阵首届鸿蒙生态大会成功举办

11月23日中国深圳&#xff0c;首届鸿蒙生态大会上&#xff0c;软通动力及软通动力子公司鸿湖万联作为全球智慧物联网联盟&#xff08;GIIC&#xff09;理事单位、鸿蒙生态服务&#xff08;深圳&#xff09;有限公司战略合作伙伴&#xff0c;联合软通教育深度参与了大会多项重磅…

动手学深度学习10.5. 多头注意力-笔记练习(PyTorch)

本节课程地址&#xff1a;多头注意力代码_哔哩哔哩_bilibili 本节教材地址&#xff1a;10.5. 多头注意力 — 动手学深度学习 2.0.0 documentation 本节开源代码&#xff1a;...>d2l-zh>pytorch>chapter_multilayer-perceptrons>multihead-attention.ipynb 多头注…

故障诊断 | Transformer-LSTM组合模型的故障诊断(Matlab)

效果一览 文章概述 故障诊断 | Transformer-LSTM组合模型的故障诊断(Matlab) 源码设计 %% 初始化 clear close all clc disp(此程序务必用2023b及其以上版本的MATLAB!否则会报错!) warning off %

用html+jq实现元素的拖动效果——js基础积累

用htmljq实现元素的拖动效果 效果图如下&#xff1a; 将【item10】拖动到【item1】前面 直接上代码&#xff1a; html部分 <ul id"sortableList"><li id"item1" class"w1" draggable"true">Item 1</li><li …

单片机学习笔记 12. 定时/计数器_定时

更多单片机学习笔记&#xff1a;单片机学习笔记 1. 点亮一个LED灯单片机学习笔记 2. LED灯闪烁单片机学习笔记 3. LED灯流水灯单片机学习笔记 4. 蜂鸣器滴~滴~滴~单片机学习笔记 5. 数码管静态显示单片机学习笔记 6. 数码管动态显示单片机学习笔记 7. 独立键盘单片机学习笔记 8…

计算机视觉硬件知识点整理六:工业相机选型

文章目录 前言一、工业数字相机的分类二、相机的主要参数三、工业数字摄像机主要接口类型四、选择工业相机的考量因素六、实例分析 前言 随着科技的不断进步&#xff0c;工业自动化领域正经历着前所未有的变革。作为工业自动化的重要组成部分&#xff0c;工业相机在工业检测、…