Redis——网络模型

目录

前言

1.用户空间和内核空间

1.2用户空间和内核空间的切换

1.3切换过程

2.阻塞IO

3.非阻塞IO

4.IO多路复用

4.1.IO多路复用过程

4.2.IO多路复用监听方式

4.3.IO多路复用-select

4.4.IO多路复用-poll

4.5.IO多路复用-epoll

4.6.select poll epoll总结

4.7.IO多路复用-事件通知机制

4.8.IO多路复用-web服务流程

5.信号驱动IO 

6.异步IO

7.同步和异步

8.Redis——网络模型

 8.1.Redis到底是单线程还是多线程?

8.2.为什么Redis要选择单线程?

8.3.Redis单线程网络模型的整个流程


前言

Redis 以其卓越的性能和灵活的特性,成为众多开发者在缓存、消息队列等场景的首选。而 Redis 强大性能的背后,其网络模型与 IO 机制发挥着关键作用数据的读写速度直接影响着用户体验。大家日常逛的电商平台,流畅加载商品详情页离不开它;刷社交软件时,点赞瞬间显示也有它的功劳 。接下来,我们就深入挖掘一下,看看 Redis 是如何通过它们实现高效运转的。

1.用户空间和内核空间

为了避免用户应用导致冲突甚至内核崩溃,用户应用与内核是分离的:

进程的寻址空间会划分两部分:内核空间,用户空间

这就代表了一个完整的32位寻址空间

那什么是寻址空间呢?

寻址空间是指计算机系统中处理器能够访问的内存地址范围。

计算机中的内存是由许多存储单元组成的,每个存储单元都有一个唯一的编号,这个编号就是地址。处理器通过地址来访问内存中的数据和指令。寻址空间就是这些地址的集合,它决定了处理器能够访问的内存大小。

我们还要在系统权限上进行划分,因为我们cpu运行的各种各样的命令里边,有一些命令风险等级比较低,有一些比较高。所以cpu会把各种各样的命令划分成四个不同的等级,Ring0风险等级最低,Ring3风险等级最高

  • 用户空间只能执行受限的命令(Ring3),而且不能直接调用系统资源,必须通过内核提供的接口来访问
  • 内核空间可以执行特权命令(Ring0),调用一切系统资源

1.2用户空间和内核空间的切换

应用程序在用户空间,内核应用在内核空间,但是我们一个进程它在执行过程中因为业务比较多,可能会执行一些普通命令和特权命令调用系统资源。因此进程会在用户空间和内核空间之间进行一个转换

linux系统为了提高io效率,会在用户空间和内核空间都加入缓冲区

  • 写数据时,要把用户缓冲数据拷贝到内核缓冲区,然后写入设备
  • 读数据时,要从设备读取数据到内核缓冲区,然后拷贝到用户缓冲区

1.3切换过程

IO在用户空间和内核空间切换流程

写数据到磁盘过程

  • 1.进程在做一些简单运算,字符串处理,之后要把数据写出到我们的磁盘需要调用我们的内核
  • 2.写数据先写到缓冲区,要往磁盘写必须要切换到内核
  • 3.切换到内核,内核没有我要写的数据,所以要先把用户缓冲区的数据拷贝到内核的缓冲区,然后再把缓冲区的数据写入我们的磁盘

读数据到用户空间

  • 1.开始用户空间发起read的请求,请求到达内核空间判断有没有数据,如果要读的是磁盘,要先去寻址wati,for data,寻址到之后把数据读取到缓冲区
  • 2.把数据从内核缓冲区拷贝到用户空间用户再区处理这些数据

从上图可知IO读写效率的主要原因是

  • 1.数据等待过程,(用户空间读数据发起read请求,内核空间接收到这个请求需要去寻址和把数据写到缓冲区)
  • 2.就是数据拷贝过程非常影响性能,一个空间缓冲区数据拷贝到另一个空间缓冲区

数据拷贝是由操作系统内核来完成的。

2.阻塞IO

不同IO模型的差别就是在1.等待数据就绪 2.读取数据过程

阻塞IO就是两个阶段都必须阻塞等待;

所以阻塞IO就是2.把数据写到内核缓冲区1.把内核缓冲区数据写到用户空间缓冲区这两个阶段都是阻塞状态。

3.非阻塞IO

顾名思义,非阻塞IO的recvfrom操作会立即返回结果而不是阻塞用户进程。

非阻塞IO就是第一阶段不停调用recvfrom去读取数据读取不到不会阻塞一直反复调用直到成功(反而让cpu使用率增加)

然后第二阶段内核拷贝数据到用户空间依然是阻塞状态

4.IO多路复用

无论是阻塞IO还是非阻塞IO,用户应用在一阶段都需要调用recvfrom来获取数据,差别在于无数据时的处理方案:

  • 如果调用recvfrom时,恰好没有数据,阻塞IO会使进程阻塞,非阻塞IO使CPU空转,都不能充分发挥CPU的作用。
  • 如果调用recvfrom时,恰好有数据,则用户进程可以直接进入第二阶段,读取并处理数据

比如服务端处理客户端Socket时,在单线程情况下,只能依次处理每一个socket,如果正在处理的socket恰好未就绪(数据不可读或不可写),线程就会被阻塞,所有其它客户端socket都必须等待,性能自然会很差。

解决方案就是数据就绪了,用户应用就去读取数据

用户进程如何知道内核中数据是否就绪呢?

4.1.IO多路复用过程

文件描述符(File Descriptor):简称FD,是一个从0开始递增的无待号整数,用来关联Linux中的一个文件。在Linux中,一切皆文件,例如常规文件,视频,硬件设备等,当然也包括网络套接字(socket)。


IO多路复用:是利用单个线程来同时监听多个FD,并在某个FD可读,可写时得到通知,从而避免无效的等待,充分利用CPU资源

IO多路复用过程:

  1. 用户应用首先去调用select函数,不在是调用recvfrom(recvfrom直接是尝试读取数据读的目标是具体某一个FD)
  2. select函数内部可以接收多个FD,也就是说可以把每个客户端socket对应的FD,传给select函数,然后传入到内核中,内核就可以去检查你要去监听的多个FD,有没有任何一个是就绪的,只要由任意一个或者多个就绪就会直接返回这个结果。
  3. 如果这n个FD都没有就绪那么就会稍微等一会,在等待过程中其实就会有后台进程去监听这些FD,一旦有任意一个或者多个就绪返回结果。这个等待不可避免的。
  4. 拿到readable结果了就去调用recvfrom我们可以明确知道哪些FD准备好啦,然后拷贝数据返回结果(循环调用)

其实IO多路复用1.等待数据就绪用户进程也是阻塞阶段,阶段二数据拷贝同样是阻塞的

区别在于:

  • 阻塞IO调用的是调用recvfrom去监听某一个FD有没有就绪,没有就会阻塞
  • IO多路复用调用的是select函数去监听多个FD,只要有一个FD就绪就去处理

4.2.IO多路复用监听方式

IO多路复用监听FD的方式,通知的方式又有多种实现,常见的有:

select

poll

epoll

差异:

  • select和poll只会通知用户进程有FD就绪,但不确定具体是哪个FD,需要用户进程逐个遍历FD来确认
  • epoll则会在通知用户进程FD就绪的同时,把已就绪的FD写入用户空间

4.3.IO多路复用-select

select是Linux中最早的IO多路复用实现方案:

select函数:

1.nfds:这是需要监视的最大文件描述符加 1。举例来说,若要监视的文件描述符为 3、4、5,那么nfds的值就是5 + 1 = 6

为了能更精细地管理和监控不同类型的 IO 事件,select将FD分成三个集合

2.readfds:该集合用于监视文件描述符的可读事件。

3.writefds:此集合用于监视文件描述符的可写事件。

4.exceptfds:这个集合用于监视文件描述符的异常事件。

执行流程:

首先用户空间

  • 1.创建fd_set rfds集合
  • 2.fd_set集合底层使用fds_bits[]来监听,共可以监听1024个Fd,要监听哪个就把哪个位置变为1。比如要监听fd = 1,2,5
  • 3.执行select函数,把fds_bits[]数组拷贝到内核空间

内核空间

  • 1.首先遍历fd_set看有没就绪
  • 2.没有就绪则睡眠,后台监听
  • 3.等待数据就绪被唤醒或超时
  • 4.fd = 1数据就绪,其他没有就绪剔除,返回结果有几个就绪了,拷贝到用户空间遍历哪个就绪了

处理完之后再次把要监听数据放到集合里执行select去监听往复处理数据

缺点:

  • 1.需要将整个fd_set从用户空间拷贝到内核空间,select结束还要再次拷贝回用户空间
  • 2.select无法得知具体哪个FD就绪,需要遍历整个FD_set,
  • 3.fd_set监听的fd数量不能超过1024

4.4.IO多路复用-poll

poll模式对select模式做了简单改进,但性能提升不明显,部分关键代码如下:

poll参数部分和select差不多主要区别在于fds,pollfd数组,没有去划分不同事件集合全部划分到一个数组当中。区别是哪种事件主要在于结构体中 events属性不同值代表不同监听类型

revents表示实际发生的事件类型,内核会把就绪的事件类型放在这个集合里,超时时间过了还没有就绪FD,就把这个值给成0返回给用户空间,这样一来就知道这个FD有没有发生事件。

IO流程:

1.创建pollfd数组,向其中添加关注的fd信息,数组大小自定义
2.调用poll函数,将pollfd数组拷贝到内核空间,转链表存储,无上限
3.内核遍历fd,判断是否就绪
4.数据就绪或超时后,拷贝pollfd数组到用户空间,返回就绪fd数量n
5.用户进程判断n是否大于0
6.大于0则遍历pollfd数组,找到就绪的fd

与select相比:

  • select模式中的fd_set大小固定为1024,而pollfd在内核中采用链表,理论上无上限
  • 监听fd越多,每次遍历消耗时间也越久,性能反而会下降

4.5.IO多路复用-epoll

epoll模式是对select和poll的改进,它提供了三个函数:

eventpoll结构体:使用红黑树存储要监听的FD,和使用链表记录就绪的FD

2.接下来我们就要去监听FD了,会使用到第二个函数

这个函数epoll_ctl将我们一个FD添加到eventpoll里面,相当于监听FD

传入的参数包括(需要添加到哪个eventpoll,是增删改哪个操作,要监听的fd,监听事件类型

3.第三个函数就是等待FD的监听就绪。函数传入参数(eventpoll指针,空数组用于接收就绪的FD,events数组最大长度,超时时间)返回就绪的数量

那么我怎么知道哪个FD就绪了呢?空数组就派上用场了

我们把这个数组拷贝到用户空间就知道哪些FD就绪了,不用去遍历

4.6.select poll epoll总结

select模式存在的三大问题:

  • 能监听的FD最大不超过1024
  • 每次select都需要把所有要监听的FD都拷贝到内核空间
  • 每次都要遍历所有FD来判断就绪状态

poll模式的问题:

poll模式利用链表解决了select中监听FD上限问题,但依然要遍历所有FD,如果监听较多,性能会下降

epoll模式中如何解决这些问题的?

  • 基于epoll实例中的红黑树保存要监听的FD,理论上无上限,而且增删改查效率都非常高,性能不会随监听FD数量增多而下降
  • 每个FD只需要执行一次epoll_ctl添加到红黑树,以后每次epol_wait无需传递任何参数,无需重复拷贝到Fd到内核空间
  • 内核会将就绪的FD拷贝到用户空间的知道位置,用户进程无需遍历所有FD就知道就绪的FD是谁

4.7.IO多路复用-事件通知机制

但FD有数据可读时,我们调用epoll_wait就可以得到通知。但是事件通知的模式有两种:

  • LevelTriggered:简称lLT.当FD有数据可读时,会重复通知多次,直至数据处理完成。是epoll的默认模式。
  • EdgeTriggered:简称LT.当FD有数据可读时,只会被通知一次,不管数据是否处理完成。

举个栗子:

  1. 假设一个客户端socket对应的fd已经注册到了epoll实例中
  2. 客户端socket发送了2kb的数据
  3. 服务端调用epoll_wait,得到通知说fd就绪
  4. 服务端从fd读取了1kb数据
  5. 回到步骤3(再次调用epoll_wait,形成循环)

总结:

ET模式避免了LT模式可能出现惊群现象

ET模式最好结合非阻塞IO读取FD数据,相比LT会复杂一些

4.8.IO多路复用-web服务流程

serverSocket:接收客户端请求

5.信号驱动IO 

信号驱动IO是内核建立SIGIO的信号管理并设置回调,但内核有FD就绪时,会发出SIGIO信号通知用户,期间用户可以执行其它业务,无需阻塞等待。

首先信号驱动IO第一阶段处理流程:

  1. 用户进程一上来不是调用recvfrom,而是系统调用sigaction指定FD绑定信号处理函数,立即结束不用阻塞等待,
  2. 如果没有数据内核帮我们监听如果有数据了帮我们去唤醒并且提交一个信号给我们用户进程,
  3. 之前建立的SIGIO信号处理函数就去处理这个信号,整个过程用户不用去等待可以去干其他事情

第二阶段和其他阻塞IO一致

那么你可能会问这个IO这么好为什么不用而要去用多路复用IO?

当有大量IO操作时,信号较多,SIGIO处理函数不能及时处理可能导致信号队列溢出

而且内核空间与用户空间频繁的信号交互性能也较低。

6.异步IO

异步IO的整个过程都是非阻塞的,用户进程调用完异步API后尽可以去做其他事情,内核等待数据就绪并拷贝到用户空间后才会递交信号,通知用户进程。

  • 1.异步IO并没有调用recvfrom函数而是调用aio_read通知内核说我想读哪个FD,读到哪里去就结束了。
  • 2.内核把数据准备就绪再把数据拷贝完成通知用户进程

异步IO在两个阶段都是非阻塞的

这种模式听着非常好但是高并发场景下如果对异步 I/O 的并发度控制不当,可能会导致系统资源过度使用,从而影响性能。

过多的异步网络请求可能会导致网络拥塞,降低系统的响应速度。

7.同步和异步

IO操作是同步还是异步,关键看数据在内核空间与用户空间的拷贝过程(数据读写的IO操作),也就是阶段二是同步还是异步:

 

8.Redis——网络模型

 8.1.Redis到底是单线程还是多线程?

1.如果仅仅聊Redis的核心业务部分(命令处理),答案是单线程

2.如果是聊整个Redis,那么答案就是多线程

在Redis版本迭代过程中,在两个重要的事件节点上引入了多线程的支持:

Redis v4.0:引入多线程异步处理耗时较长的任务,例如异步删除命令unlink

Redis v6.0:在核心网络模型中引入多线程,进一步提高对于多核cpu的利用率

8.2.为什么Redis要选择单线程?

1.抛开持久化不谈,Redis是纯内存操作,执行速度非常快,它的性能瓶颈是网络延迟而不是执行速度,因此多线程并不会带来巨大的性能提升

2.多线程会导致过多的上下文切换,带来不必要的开销

3.引入多线程会面临线程安全问题,必然要引入线程锁,这样的安全手段,实现复杂度增高,而且性能也会大打折扣

8.3.Redis单线程网络模型的整个流程

Redis通过IO多路复用来提高网络性能,并且支持各种不同的多路复用实现,并且将这些实现进行封装,提供了统一的高性能事件库AE:

整体来看,Redis的网络模型就是基于"IO多路复用 + 事件派发机制",总之就是客户端请求来了之后,我们都会做多路复用的事件监听,无论是什么类型的事件.然后我提前定义好各种各样的事件处理器.对于不同的事件,我就派发给不同的事件处理器来处理..

性能瓶颈:Redis6.0版本中引入了多线程,目的是为了提高IO读写效率。因此在 “解析客户端命令,写出相应结果” 时采用了多线程。核心的执行命令执行,IO多路复用模块依然采用的时由主线程执行。

1.就是Client socket有readable事件交给命令请求处理器,然后将请求数据写入querybuf并解析数据这是一个IO过程

2.触发写事件,往客户端写数据这里又会有一次网络IO,收到网络带宽,网络状态影响。

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

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

相关文章

电力系统中为什么采用三相交流电?

电力系统中为什么采用三相交流电 电力系统中采用三相交流电,主要是因为它在输电效率、设备使用、能量传输平稳性等方面相比单相交流或直流电具有显著优势。下面我详细解释一下原因: 🌟 1. 提高输电效率(节省电缆材料)…

python简介与入门

目录 python初始 python的优势 python的特性 python的应用领域 Linux环境中安装python 下载python3.11.6 安装依赖包 解压python压缩包 安装python 编译及安装 建立软连接 测试python3运行 设置国内pip更新源 更新pip版本(必须更新) wind…

Web内网渗透知识大全

内网渗透 端口转发 需要三个主机,Windows7、Windows2016、kali Windows7为内网主机 Windows2016为跳板机 kali为攻击机 使用到的工具 netsh、MSF 我们先在Windows7上开启一个80服务,而这个80服务只能由Windows2016访问,kali不能访问 我们…

ffmpeg av_buffer_unref的逻辑实现; av_freep 和 av_freep函数的区别

av_buffer_unref 是 FFmpeg 中用于管理引用计数和内存释放的核心函数,其内部实现机制如下: ‌一、核心流程‌ ‌引用计数递减‌ 函数首先对 AVBufferRef 的 buffer->refcount 进行原子递减操作(通过 atomic_fetch_add_explicit 等机制保证…

从 GPS 数据中捕捉城市休闲热点:空间异质性视角下的新框架

从 GPS 数据中捕捉城市休闲热点:空间异质性视角下的新框架 原文:Capturing urban recreational hotspots from GPS data: A new framework in the lens of spatial heterogeneity 1. 背景与意义 城市娱乐活动的重要性: 娱乐活动是城市生活…

rk3568main.cc解析

rk3568main.cc解析 前言解析前言 正点原子rk3568学习,rk官方RKNN_MODEL_ZOO文件中 rknn_model_zoo-main/examples/mobilenet/cpp/main.cc 从执行命令:./build-linux.sh -t rk3568 -a aarch64 -d mobilenet 到: cmake ../../examples/mobilenet/cpp \-DTARGET_SOC=rk356x\…

【实验数据处理matlab程序】程序1:绘制figure文件中曲线的RMS值

立意 在本课题所涉及的实验中,需要将2个拉线式位移传感器中的数据收集并处理,在此基础上求解相应的速度 主要功能 针对一个figure文件中仅包含一个plot,且该plot中包含指定数目的曲线,求这些曲线的RMS值;针对一个fi…

kotlin的kmp编程中遇到Unresolved reference ‘java‘问题

解决办法 打开 File → Project Structure → Project 确保 Project SDK 是 与你的 jvmToolchain 保持一致 如果没有,点击右上角 Add SDK 添加 JDK 路径 同步Sync 然后就正常了。 package org.example.projectimport androidx.compose.animation.AnimatedVi…

静电放电测试中垂直和水平耦合板的作用

在静电放电(ESD,Electrostatic Discharge)测试中,垂直耦合板(Vertical Coupling Plane, VCP)和水平耦合板(Horizontal Coupling Plane, HCP)是模拟设备在实际环境中因静电放电产生的…

Web开发-JavaEE应用JNDI注入RMI服务LDAP服务DNS服务高版本限制绕过

知识点: 1、安全开发-JavaEE-JNDI注入-LADP&RMI&DNS等 2、安全开发-JavaEE-JNDI注入-项目工具&手工原理等 演示案例-WEB开发-JavaEE-JNDI注入&LDAP&RMI服务&DNS服务&高版本限制绕过 JNDI全称为 Java Naming and DirectoryInterface&am…

基于Transformer与随机森林的多变量时间序列预测

哈喽,我不是小upper,今天和大家聊聊基于Transformer与随机森林的多变量时间序列预测。 不懂Transformer的小伙伴可以看我上篇文章:一文带你彻底搞懂!Transformer !!https://blog.csdn.net/qq_70350287/article/detail…

初识网络原理

文章目录 1.IP地址2.端口号3.网络中的五元组4.协议分层5.封装和分用6.客户端与服务器 1.IP地址 IP地址 (IP Address): 用于在网络中唯一标识一台设备(主机、路由器等)。IP地址分为IPv4和IPv6两种版本。IPv4是32位地址,通常表示为四个用点分隔…

健康养生之道

健康养生是一种积极的生活态度,是对自己和家人的关爱。从生活的细微之处做起,便能为健康打下坚实的基础。 在生活习惯方面,要注重细节。保持居住环境的整洁与通风,让生活空间舒适宜人。勤洗手、勤换洗衣物,养成良好的卫…

几种电气绝缘类型

1. 基本绝缘 1.1 绝缘等级 1.2 I类设备 2. 附加绝缘 3. 双重绝缘 4. 加强绝缘 5. 功能性绝缘 1. 基本绝缘 用于防止触及带电部件的初级保护,该防护是由绝缘材料完成的 基本绝缘的目的在于为防电击提供一个基本的保护,以避免触电的危险,不过此类绝缘只能保证正常状态下…

01.浏览器自动化webdriver源码分析之启动函数

日后,网络爬虫也好,数据采集也好,自动化必然是主流。因此,笔者未雨绸缪,在此研究各类自动化源码,希望能够赶上时代,做出一套实用的自动化框架。 这里先研究传统的webdriver中转来进行浏览器自动…

PLOG安装

Plog可以通过以下命令安装 cd ~ && git clone https://github.com/SergiusTheBest/plog.gitcd plog && mkdir buildcd build && cmake ..make && sudo make installcd ~ && sudo rm -rf ./plog若无法科学上网,可使用git cl…

Cyber SpaceGuidance网安学习指南见解

免责声明 如有异议请在评论区友好交流,或者私信 内容纯属个人见解,仅供学习参考 如若从事非法行业请勿食用 如有雷同纯属巧合 版权问题请直接联系本人进行删改 前言 提示:这里可以添加本文要记录的大概内容: 提示:以…

第十五届蓝桥杯 2024 C/C++组 下一次相遇

目录 题目: 题目描述: 题目链接: 思路: 自己的思路详解: 更好的思路详解: 代码: 自己的思路代码详解: 更好的思路代码详解: 题目: 题目描述&#xf…

Vue3中provide和inject数据修改规则

在 Vue3 中,通过 inject 接收到的数据是否可以直接修改,取决于 provide 提供的值的类型和响应式处理方式: 1. 若提供的是普通值(非响应式数据) javascript 复制 // 父组件 provide(staticValue, 123); 子组件修改行…

今日CSS笔记

原手写笔记 ------------------------------------------------------------------------------------------------------- css选择器的种类有很多种。这里只介绍几种常用的选择器。 1. 标签选择器标签选择器是最基本的选择器,它可以选择所有的标签。例如&#xff…