Redis 网络模型

news2024/11/27 2:38:54

一、用户空间和内核空间

1.1 linux 简介

        服务器大多采用 Linux 系统,这里我们以 Linux 为例来讲解,下面有两个不同的 linux 发行版,分别位 ubuntu centos,其实发行版就是在 Linux 系统上包了一层壳。

        任何 Linux 发行版,其系统内核都是 Linux。我们在 Linux 上安装的应用,比如说 mysqlredis 等,它们没有办法直接访问计算机硬件,它们需要先访问内核,然后再基于内核去操作计算机硬件。如下图:

        再细化的说下,计算机硬件一般包括 CPU、内存、网卡等各种各样的设备,虽然内核可以操作硬件,但它也需要不同设备的驱动,在设备驱动的基础上就可以形成对内存的管理、进程的管理、文件系统的管理和网络管理等等,如下图:

        最后呢,如果我们想让用户应用来访问,就必须在设备驱动的基础上封装一些接口,这样一来,我们的用户应用,包括一些依赖库等就可以调用这些接口,从而间接的实现对硬件设备的访问。

1.2 寻址空间

        但是这里有一个问题,我们的内核本质也是个应用,它在运行的过程中也需要一些 cpu 资源,内存资源等。所以为了避免用户应用导致冲突甚至内核崩溃,用户应用与内核是分离的。

        首先把进程的寻址空间划分为两部分:内核空间、用户空间。寻址空间是什么呢?其实无论是内核还是用户应用,都无法直接访问物理内存,而是给他们分配虚拟的内存空间,映射到不同的物理内存上,这样一来,我们的内核或者用户应用再去访问虚拟内存空间的时候就需要一个虚拟的地址了,这个地址是一个无符号的整数,从 0 开始,它的最大值取决于 cpu 的地址总线和寄存器的带宽,以一个 32 位系统为例,那么它的带宽一般为 32,因此它的地址的最大值就是 2^32 ,也就是说寻址的范围就是从 02^32,这个寻找的范围就是寻址空间。

        我们内存地址的每一个值代表的就是一个存储单元,也就是一个字节。所以 2^32 这么大的寻址空间最多代表的就是 2^32 字节,也即是 4GB,这也就是为什么一个 32 位的系统它的寻址空间最多就是 4GB 的原因。而这 4GB 又会被分为两部分,内核空间和用户空间。如下图:

        光在内存上做一个划分还不够,我们还需要在系统权限上做一个划分,因为在 cpu 运行的各种命令中,有一些命令的风险等级比较低,有一些比较高。

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

        内核空间可以执行特权命令(Ring0),调用一切系统资源。

        一般来说用户的应用都是运行在用户空间,而内核的应用运行在内核空间。

         但是假设一个进程,执行的业务比较多,可能会执行一些普通的命令,也有可能需要去执行一些特权命令去调用系统资源,因此我们的进程就需要在用户空间和内核空间来回切换,当一个进程运行在用户空间的时候我们就称其为用户态,当期运行在内核空间的时候,就称其为内核态,如下图:

        所以我们的进程很有可能在两个状态之间进行切换,那么这个切换的流程到底是什么样子的呢?接下来举个 IO 读写的例子。

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

        假设此时用户空间发起请求要去读数据,这个请求到达内核空间之后先去判断有没有数据,假如此时读取的是磁盘的数据,那么此时磁盘就需要去寻址,所以在这个过程中就需要等待;如果读取的不是磁盘的数据,读取的是网卡的数据,网卡的数据是别人传过来的,如果别人还没传过来,你还是需要等待的。所以当你发送一个读的请求时,首先是要等,等数据到达。数据到达之后,先把数据读取到缓冲区里面,然后把数据拷贝回用户空间里面做处理。

        其实影响我们 IO 效率最主要的第一个原因就是需要数据的等待,第二个原因是数据的拷贝,所以要想提高 IO 的效率,就需要从这两个点出发。减少无效的等待和减少用户态和内核态之间缓冲区的拷贝。后面的五种不同的 IO 模型都是针对这两个点进行优化的。

二、阻塞 IO

        顾名思义,阻塞 IO 就是两个阶段都必须阻塞等待。这个过程需要经历两个阶段。

阶段一:

        1、用户进程尝试读取数据(比如网卡数据)

        2、此时数据尚未到达,内核需要等待数据

        3、此时用户进程也处于阻塞状态

阶段二:

        1、数据到达并拷贝到内核缓冲区,代表已就绪

        2、将内核数据拷贝到用户缓冲区

        3、拷贝过程中,用户进程依然阻塞等待

        4、拷贝完成,用户进程解除阻塞,处理数据

        可以看到,阻塞 IO 模型中,用户进程在两个阶段都是阻塞状态。

三、非阻塞 IO

        顾名思义,非阻塞 IOrecvfrom 操作会立即返回结果而不是阻塞用户进程。这个过程需要经历两个阶段。

阶段一:

        1、用户进程尝试读取数据(比如网卡数据)

        2、此时数据尚未到达,内核需要等待数据

        3、返回异常给用户进程

        4、用户进程拿到 error 后,再次尝试读取

        5、循环往复,直到数据就绪

阶段二:

        1、将内核数据拷贝到用户缓冲区

        2、拷贝过程中,用户进程依然阻塞等待

        3、拷贝完成,用户进程解除阻塞,处理数据

        可以看到,非阻塞 IO 模型中,用户进程在第一个阶段是非阻塞,第二个阶段是阻塞状态。虽然是非阻塞,但性能并没有得到提高。而且忙等机制会导致 CPU 空转,CPU 使用率暴增。 

四、IO 多路复用

4.1 简介

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

        1、如果调用 recvfrom 时,恰好没有数据,阻塞 IO 会使 CPU 阻塞,非阻塞 IO 使 CPU 空转,都不能充分发挥 CPU 的作用。

        2、如果调用 recvfrom 时,恰好有数据,则用户进程可以直接进入第二阶段,读取并处理数据。

        而在单线程情况下,只能依次处理 IO 事件,如果正在处理的 IO 事件恰好未就绪(数据不可读或不可写),线程就会被阻塞,所有 IO 事件都必须等待,性能自然会很差。

上面的情况就比如服务员给顾客点餐,分两步:

        1、顾客思考要吃什么(等待数据就绪)

        2、顾客想好了,开始点餐(读取数据)

        要提高效率有两种办法,第一种增加更多服务员(多线程)。第二种是不排队,谁想好了吃什么(数据就绪了),服务员就给谁点餐(用户应用就去读取数据)。那么用户进程如何知道内核中数据是否就绪呢?此时就需要用到 FD 了。

4.2 文件描述符 FD

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

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

阶段一:

        1、用户进程调用 select,指定要监听的 FD 集合。

        2、内核监听 FD 对应的多个 socket

        3、任意一个或多个 socke t数据就绪则返回 readabl

        4、此过程中用户进程阻塞

阶段二:

        1、用户进程找到就绪的 socket

        2、依次调用 recvfrom 读取数据

        3、内核将数据拷贝到用户空间

        4、用户进程处理数据

        IO 多路复用是利用单个线程来同时监听多个 FD,并在某个 FD 可读、可写时得到通知,从而避免无效的等待,充分利用 CPU 资源。不过监听 FD 的方式、通知的方式又有多种实现,常见的有:selectpollepoll。它们之间的差异如下:

        1、select poll 只会通知用户进程有 FD 就绪,但不确定具体是哪个 FD,需要用户进程逐个遍历 FD 来确认。

        2、epoll 则会在通知用户进程 FD 就绪的同时,把已就绪的 FD 写入用户空间。

五、信号驱动 IO

        信号驱动 IO 是与内核建立 SIGIO 的信号关联并设置回调,当内核有 FD 就绪时,会发出 SIGIO 信号通知用户,期间用户应用可以执行其它业务,无需阻塞等待。这个过程可以分为两个阶段。

阶段一:

        1、用户进程调用 sigaction,注册信号处理函数

        2、内核返回成功,开始监听 FD

        3、用户进程不阻塞等待,可以执行其它业务

        4、当内核数据就绪后,回调用户进程的 SIGIO 处理函数

阶段二:

        1、收到 SIGIO 回调信号

        2、调用 recvfrom,读取数据。

        3、内核将数据拷贝到用户空间

        4、用户进程处理数据

        当有大量 IO 操作时,信号较多,SIGIO 处理函数不能及时处理可能导致信号队列溢出,而且内核空间与用户空间的频繁信号交互性能也较低。

六、异步 IO

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

阶段一:

        1、用户进程调用 aio_read,创建信号回调函数

        2、内核等待数据就绪

        3、用户进程无需阻塞,可以做任何事情

阶段二:

        1、内核数据就绪

        2、内核数据拷贝到用户缓冲区

        3、拷贝完成,内核递交信号触发 aio_read 中的回调函数

        4、用户进程处理数据

        可以看到,异步 IO 模型中,用户进程在两个阶段都是非阻塞状态。

七、同步还是异步

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

八、Redis 网络模型

8.1 Redis 是否为单线程

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

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

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

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

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

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

        因此,对于 Redis 的核心网络模型,在 Redis 6.0 之前确实都是单线程。是利用 epollLinux 系统)这样的 IO 多路复用技术在事件循环中不断处理客户端情况。

为什么 Redis 要选择单线程?

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

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

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

8.2 Redis 网络模型

        Redis 6.0 版本中引入了多线程,目的是为了提高 IO 读写效率。因此在解析客户端命令、写响应结果时采用了多线程。核心的命令执行、IO 多路复用模块依然是由主线程执行。如下图:

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

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

相关文章

【排序算法】希尔排序详解(C语言)

文章目录 前言希尔排序的原理原理思路 代码实现希尔排序的相关问题效率算法稳定性 前言 为什么会有希尔排序,要从插入排序说起,希尔排序一开始设计出来是为了改进插入排序,因为插入排序在处理大量数据时效率不高,特别是对于近乎有…

结合Boosting理论与深度ResNet:ICML2018论文代码详解与实现

代码见:JordanAsh/boostresnet: A PyTorch implementation of BoostResNet 原始论文:Huang F, Ash J, Langford J, et al. Learning deep resnet blocks sequentially using boosting theory[C]//International Conference on Machine Learning. PMLR, 2…

Bagging与Boosting的应用与优势

Hi~!这里是奋斗的小羊,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~ 💥💥个人主页:奋斗的小羊 💥💥所属专栏:C语言 🚀本系列文章为个人学习…

深浅拷贝以及正则表达式(python)

浅拷贝和深拷贝: 浅拷贝: copy函数是浅拷贝,支队可变类型的第一层对象进行拷贝,对拷贝的对象开辟显得内存空间进行存储,不会拷贝对象内部的子对象 不可变类型的浅拷贝示例: 浅拷贝不会对不可变类型进行…

KVB:怎么样选择最优交易周期?

摘要 在金融交易中,周期的选择是影响交易成败的重要因素之一。不同的交易周期对应不同的市场环境和交易策略,选择合适的周期可以提高交易的成功率。本文将详细探讨交易中如何选择最优周期,包括短周期、中周期和长周期的特点及适用情况&#…

ssm宠物网站系统-计算机毕业设计源码07183

摘 要 在信息飞速发展的今天,网络已成为人们重要的信息交流平台。宠物网站每天都有大量的信息需要通过网络发布,为此,本人开发了一个基于B/S(浏览器/服务器)模式的宠物网站系统。 该系统以JJava编程语言、MySQL和SSM框…

Linux自旋锁

面对没有获取锁的现场,通常有两种处理方式。 互斥锁:堵塞自己,等待重新调度请求自旋锁:循环等待该锁是否已经释放 本文主要讲述自旋锁 自旋锁其实是一种很乐观的锁,他认为只要再等一下下锁便能释放,避免…

最适合程序员的编程字体,漂亮、独特、优雅!(2024-06-17)

Monaco Monaco 字体是一款专为编程和代码编辑设计的等宽字体,以其简洁明了的无衬线设计风格、高可读性和清晰的字符区分度,受到开发者们的青睐,Mac 自带 Monaco 字体。 Consolas Consolas 是一款等宽无衬线字体,专为编程和代码编…

vscode c++ 开发环境配置

今天各位同学已经安装了mingw环境,但部分同学vscode开发环境又问题,究其原因,还是vscode 编译环境配置错误,有问题的同学 按如下步骤处理: 1、卸载相关插件,特别是中文插件,原因是暂时回避中文…

Unity2D游戏制作入门 | 13 ( 之人物三段攻击 )

上期链接:Unity2D游戏制作入门 | 12(之人物受伤和死亡的逻辑动画)-CSDN博客 上期我们聊了人物的受伤和死亡的逻辑和动画,我们主要学习了事件的执行,即我们在人物受伤时可能会触发很多的事件,比如触发人物受伤的动画以及播放音乐等…

定个小目标之刷LeetCode热题(16)

针对本题排序流程,主要是将链表拆分为长度为subLength的子链表1和子链表2,然后把子链表1和子链表2合并为一条有序链表,重复上述步骤直到把链表都拆分完,这样这条链表每段长度为2的子链表都是有序的,那么要整条链表有序…

Ubuntu的文件权限介绍

Linux系统是一个多用户系统,每个用户都会创建自己的文件。为了防止其他人擅自改动他人的文件,需要拥有一套完善的文件保护机制。在Linux系统中,这种保护机制就是文件的访问权限。文件的访问权限决定了谁可以访问和如何访问特定的文件。 为了…

【设计模式-12】代理模式的代码实现及使用场景

 代理模式是一种应用很广发的结构性设计模式,它的设计初衷就是通过引入新的代理对象,在客户端和目标对象之间起到中介的作用,从而实现控制客户端对目标对象的访问,比如增强或者阉割某些能力。 1. 概述 代理模…

一行代码实现鼠标横向滚动

🧑‍💻 写在开头 点赞 收藏 学会🤣🤣🤣 在项目中我们可能会遇到当鼠标在某个区域内,我们希望滚动鼠标里面的内容可以横向滚动; 比如我们一些常见的后台状态栏: 那这种该怎么写&…

使用 calibre 拆分电子书合辑

文章目录 引言下载插件拆书设置封面等元信息 引言 下载电子书合辑后,想拆分为单独成册的文件 https://bookfere.com/post/603.html 教程使用 calibre 的 EpubSplit 插件,这里我跟着实践,记录在此,希望能帮助你。 本文基于 macOS …

llamaindex原理与应用简介(宏观理解)

llamaindex原理与应用简介(宏观理解) 文章目录 llamaindex原理与应用简介(宏观理解) 这是我认为对于 llamaindex 应用的场景概述讲的相对比较好的视频:llamaindex原理与应用简介

PLSQL、Oracle以及客户端远程连接服务器笔记(仅供参考)

1.PLSQL参考链接: 全网最全最细的PLSQL下载、安装、配置、使用指南、问题解答,相关问题已汇总-CSDN博客文章浏览阅读2.9w次,点赞98次,收藏447次。双击之后,这里选择安装目录,你安装目录选的哪里&#xff0…

第三方软件测试报告包括哪些内容?如何获取专业第三方测试报告?

第三方软件测试报告是由独立的第三方公司进行软件测试后所生成的报告。该报告会清晰地呈现出软件在各个方面的测试结果和评估。通过第三方公司的专业测试,这些报告具有公正、中立和权威的特点。 一、第三方软件测试报告包括哪些内容? 1、功能测试:验证…

Day 44 Ansible自动化运维

Ansible自动化运维 几种常用运维工具比较 ​ Puppet ​ —基于 Ruby 开发,采用 C/S 架构,扩展性强,基于 SSL,远程命令执行相对较弱ruby ​ SaltStack ​ —基于 Python 开发,采用 C/S 架构,相对 puppet 更轻量级,配置语法使用 YAML,使得配置脚本更简单 ​ Ansible ​ —基于 …