Redis网络模型、通信协议、内存回收

news2024/11/16 7:49:26

Redis网络模型

  • 一、用户空间和内核空间(前提)
      • 问题来了:为啥要区分用户空间和内核空间呢?
      • 我们来看看两个空间以及硬件是如何操作的
  • 二、Linux中五种IO模型
    • 1、 阻塞IO
    • 2、非阻塞IO
    • 3、IO多路复用
      • 3.1、SELECT
      • 3.2、poll
      • 3.3、epoll
    • 4、信号驱动IO
    • 5、异步IO
  • 三、Redis中的网络模型
  • 四、Redis通信协议
    • 定义
    • RESP2协议-数据类型
  • 五、Redis内存回收

一、用户空间和内核空间(前提)

在这里插入图片描述
我们知道操作系统采用的是虚拟地址空间,以32位操作系统举例,它的寻址空间为4G(2的32次方),这里解释二个概念:
寻址: 是指操作系统能找到的地址范围,32位指的是地址总线的位数,你就想象32位的二进制数,每一位可以是0,可以是1,是不是有2的32次方种可能,2^32次方就是可以访问到的最大内存空间,也就是4G。
虚拟地址空间: 为什么叫虚拟,因为我们内存一共就4G,但操作系统为每一个进程都分配了4G的内存空间,这个内存空间实际是虚拟的,虚拟内存到真实内存有个映射关系。例如X86 cpu采用的段页式地址映射模型。
操作系统将这4G可访问的内存空间分为二部分,一部分是内核空间,一部分是用户空间
内核空间是操作系统内核访问的区域,独立于普通的应用程序,是受保护的内存空间
用户空间是普通应用程序可访问的内存区域。
以linux(32位操作系统)为例:
将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空间,而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为用户空间
在这里插入图片描述
每个进程可以通过系统调用进入内核,因此,Linux内核由系统内的所有进程共享。于是,从具体进程的角度来看,每个进程可以拥有4G字节的虚拟空间。

问题来了:为啥要区分用户空间和内核空间呢?

其实早期操作系统是不区分内核空间和用户空间的,但是应用程序能访问任意内存空间,如果程序不稳定常常把系统搞崩溃,比如清除操作系统的内存数据。后来觉得让应用程序随便访问内存太危险了,就按照CPU 指令的重要程度对指令进行了分级,指令分为四个级别:Ring0~Ring3 (和电影分级有点像),linux 只使用了 Ring0 和 Ring3 两个运行级别,进程运行在 Ring3 级别时运行在用户态,指令只访问用户空间,而运行在 Ring0 级别时被称为运行在内核态,可以访问任意内存空间。
用户态的程序不能随意操作内核地址空间,这样对操作系统具有一定的安全保护作用。

我们来看看两个空间以及硬件是如何操作的

在这里插入图片描述
**写数据到磁盘:**首先将数据写到用户空间的缓冲区,然后调用内核,内核则将数据从空间缓冲区copy到内核缓冲区,然后再将缓冲区中的数据写入磁盘。
从网络中或者磁盘中读数据 :进程首先切换到内核态,调用内核从网卡或者磁盘中读,但是没有的时候就需要等待,有则将数据读到内核的缓冲区再copy到用户区使用。
其实这里就有很多空间进行IO优化了,比如没有数据读的时候需要一直等待(一直占用cpu资源),以及读写的多次拷贝(两个空间的来回切换)等等。

二、Linux中五种IO模型

1、 阻塞IO

顾名思义:没有数据的时候一直等待,有数据则拷贝到用户空间进行处理。
在这里插入图片描述

2、非阻塞IO

顾名思义:没有就返回错误,但是会一直请求,花里胡哨其实一点用也没有,用户还是停留在访问等待直到有数据(盲目等待),拷贝到用户空间进行处理。
注意:由于没有数据的时候一直访问,cpu一直执行指令可能性能更低(cpu空转),甚至不如阻塞IO。
在这里插入图片描述

3、IO多路复用

看到这里大家发现,其实不管阻塞IO还是非阻塞IO,第一阶段都需要等待数据(恰好没数据),但是当多个进程来时,等待会影响整个业务,下面用个生活中的例子来表示,排队点餐。
在这里插入图片描述
如果第一顾客还没想好自己要吃啥的时候,后面的顾客都需要等待(等待数据)
如果已经想好了,开始点餐(读取数据)
那我们如何提高点单速率呢?

  • 多加几个前台(多线程)
  • 不排队,谁想好了吃啥,服务员就先给谁点单(读数据)

显然第二种更好一点,不需开多线程去提高效率。

问题又来了:如何知道顾客已经想好了(数据就绪?)
这里需要了解一个概念-文件描述符(File Decriptor)简称FD,是一个从0开始的无符号整数(自然数),用来关联Linux中的一个文件。在Linux中一切皆文件,常规文件,视频,硬件等待,当然包括网络套接字(Socket)。
IO多路复用: 是利用单线程来同时监听多个FD,并且在某个FD可读、可写时得到通知,从而避免无效的等待,充分利用了CPU的资源。
在这里插入图片描述
在等待数据方面,用户调用select方法,同时监听多个FD,如果都没有数据则等待(也就是所有顾客都没有想好吃啥)这个时候才阻塞等待,当有一个想好了,就可以进行点餐。从而避免了没有数据的时候直接调用recvfrom函数去内核空间中等待,浪费cpu资源。

那我们再深一层解析一下监听和通知
方式有三种 一个是上面说的select 还有poll 和epoll 他们有啥区别吗?
依旧使用前面的生活案例来解释:
在这里插入图片描述
select和poll: 当有顾客想好了,然后通知给点单前台,但是前台是不知道具体哪一位顾客准备好了,然后就一个一个问顾客(遍历),找到了再进行点单。
其实他的缺点也看到了就是有人下单了,无法立即确定是哪一个顾客。
epoll: 有一个顾客想好后,会显示到点单员电脑上,直接下单准备食物即可。
转换成计算机的话:

  • select和poll:只会通知用户进程有FD就绪,但是不确定是哪一个FD,需要逐个遍历FD来确定
  • epoll :则会通知用户进程FD就绪的同时,把已就绪FD写入用户空间中。更高效

3.1、SELECT

上源码:
在这里插入图片描述
本质是数组存二进制位,0表示未就绪,1则表示就绪,共有三个,一个是监听读事件,一个写数件,还一个是异常事件(1024位)。
select函数同时监听这三者的状态。
流程如图(假设8个位置)我们假设监听FD为1,2,5。
在这里插入图片描述
过程: 执行select函数,需要内核空间帮我监听,拷贝数组到内核空间,遍历数组,没有则休眠,有则唤醒,并且传回用户空间(又将数组拷贝回去),然后用户空间再遍历一次找到相关的就绪FD。
不足:

  • 执行select需要拷贝一份到内核空间,监听完之后又要拷贝一份到用户空间(一次监听就要2次切换),在监听后续的fd又需要重复以上操作。
  • 内核空间监听需要遍历整个数组,监听到返回给用户空间又要遍历一遍数组查找就绪fd(两次遍历)
  • 大小不能超过1024,若超过需要修改源码(很麻烦)

3.2、poll

直接上源码
在这里插入图片描述
其实和select的数据结构差不多只是数组中添加了一个状态,从而无需三个数组(换汤不换药)

执行流程:
1、创建数组pollFD,添加监听的fd信息,数组大小自定义
2、调用poll函数,将数组拷贝到内核空间,转链表存储(无上限)
3、内核遍历,判断是否就绪
4、数据就绪或者超时后,拷贝数组到用户空间,返回就绪数量n
5、使用进程判断n大于零就开始遍历数组找就绪的fd
其实本质和select没啥区别,只不过数组自定义可以大于1024个fd同时监听,内核中采用链表存储,再一个fd越多遍历越慢,性能反而下降。(意义不大,所以很少使用这种方式)

3.3、epoll

上源码
在这里插入图片描述
底层结构和过程:本质是由一个红黑树和一个链表组成,调用创建函数(epoll_create)创建并且返回,epoll_ctl()函数将fd加入到红黑树中(监听作用)并且设置回调函数,一旦回调函数触发就将该fd添加到链表(list_head)中,再通过epoll_wait()函数检查链表,有则返回就绪fd数量,并且将链表中fd复制到events数组中给用户空间使用。
在这里插入图片描述
我们将之前select的不足截取过来对比:
在这里插入图片描述

最后总结: 我们来看看相对前面二者的不足,epoll做了啥优化。

  • 对于解决监听上限的问题:基于epoll是使用红黑树存储fd,理论上数据可以很大而且红黑树查询性能不受很大的影响。
  • 对于每次监听都需要将数组拷贝到内核空间:epoll只需要执行一次epoll_ctl()就将所有fd存入红黑树中,以后每次添加fd元素即可,在等待就绪时函数epoll_wait()无需传参,无需重新拷贝fd数组到内核空间。
  • 对于将就绪的fd拷贝回用户空间:epoll无需遍历所有数组去找就绪的,而是返回的都是就绪的。

4、信号驱动IO

是与内核简历sigIO的信号关联并且设置回调,当有fd就绪就会发出sigIO信号通知用户,期间用户可以执行其他业务,无需阻塞等待。
在这里插入图片描述
特点:可以看到在等待数据阶段,是直接交给内核空间用户空间不管的
这里需要和非阻塞IO区分,等待阶段非阻塞IO是一直询问有木有数据(本质还是阻塞)
为啥我们不使用他呢?(有啥缺点?)

  • 在大量IO操作的时候,信号较多,sigIO函数不能及时处理可能导致信号队列溢出
  • 内核空间和用户空间的频繁信号交互性能也较低

5、异步IO

整个过程都是非阻塞的,用户进程调用完异步API就去做别的事情,数据等待和拷贝都是异步执行(一条龙服务)。
在这里插入图片描述
使用相对多一点,但是还是有个缺点:如果io多了,内核IO性能不是很高,导致内存消耗过多导致整个崩溃(就像老板一直把事情交给你,不管你死活)

总结:
前四种IO都是同步IO,只有最后一个是异步IO(以数据拷贝是否阻塞为基准)
在这里插入图片描述

三、Redis中的网络模型

在这里插入图片描述
简单解释:
底层就是使用IO多路复用+事件派发,首先是服务端可读,连接应答处理器将客户端socket(FD)注册到IO多路复用程序,进行客户端的读写监听,当客户端需要操作(就绪) 时(也就是客户端可读),会使用命令请求处理器,首先将请求的数据(Redis6.0前是单线程,之后引入多线程)写入缓冲区,再将数据转化为redis命令并且执行,将返回值写入缓冲区,当多个操作后就将多个client写入队列,再通过遍历队列绑定命令回复处理器,Redis6.0之前是单线程逐个处理,之后引进多线程提升了回复效率。

引入多线程: 对于redis来说在监听fd,以及命令执行的时候,(主线程)单线程是完成足够的(纯内存),真正影响性能的永远是IO,就是读命名和回复响应值(来回的拷贝)占用网络资源。

四、Redis通信协议

定义

Redis是一个CS架构软件,通信一般分两步:

  • 客户端(Client)向服务端(Server)发生一条命令。
  • 服务端解析并且执行命令,返回响应结果给客户端。

因此二者之间数据交互需要有个格式规范,这个规范就是通信协议。
而Redis中采用的就是RESP(Redis Serialization Protocol)协议

  • Redis1.2版本引入该协议
  • Redis2.0版本成为标准,称为RESP2
  • Redis6.0版本升级为RESP3,增加了更多的新数据类型和特性–客户端缓存

但目前默认使用的是RESP2,也是我们下面学习的。

RESP2协议-数据类型

通过首字节的字符来区分不同的数据类型,常用的包括5种:

  • 单行字符串:首字节是‘ +’,后面跟上单行字符串,以(“\r\n”)结尾。如:“ok” :“+ok\r\n”

  • 错位(Error):首字母‘-’,与单行字符串一样,只不过是错误信息。如:“-Error Message\r\n”

  • 数值:首字节为‘:’,后面跟上数字格式字符串也是(“\r\n”)结尾。如:“:10\r\n”

  • 多行字符串:首字节为“$”,表示二进制安全(本质和SDS一样,记录字符串占用字节大小,内容中存在"\r\n"也没事),最大支持521MB
    在这里插入图片描述
    在这里插入图片描述

  • 数组:首字节是 *,后面跟上数组元素个数,在跟上元素,类型不限(中文3个字节)
    在这里插入图片描述

五、Redis内存回收

参考我另一篇文章–《redis面试篇》中的过期策略和淘汰策略

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

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

相关文章

北大领衔:多智能体研究登上Nature子刊

这篇笔记可以作为接EoT那篇笔记内容中某种思想内涵的延伸和实践,即均是将智能体之间的关系描述为一种拓扑连接结构下的网络化关系进行研究(贴近物理世界更加真实、自然、客观的拓扑结构),在这项研究中,更多的扩展到大规…

SpringCloud-04 OpenFeign服务调用与负载均衡

OpenFeign是一个声明式、模板化的HTTP客户端,它简化了在Java应用程序中调用RESTful API的过程。OpenFeign是Netflix开发的一个开源项目,它构建在Feign的基础上,为开发者提供了更加简单、灵活的方式来实现HTTP请求。OpenFeign的特点包括&#…

蓝桥杯真题——约翰的牛奶

输入样例&#xff1a; 8 9 10 输出样例&#xff1a; 1 2 8 9 10 本题是宽搜的模版题&#xff0c;不论怎么倒牛奶&#xff0c;A,B,C 桶里的牛奶可以看做一个三元点集 我们只要找到A桶是空的&#xff0c;B,C桶中的状态即可 #include <iostream> #include <cstring…

四、滑动窗口-算法总结

文章目录 四、滑动窗口4.1 模板4.2 示例4.2.1 最小覆盖子串4.2.2 字符串的排列4.2.3 找到字符串中所有字母异位词4.2.4 无重复字符的最长子串 四、滑动窗口 4.1 模板 /* 滑动窗口算法框架 */ void slidingWindow(string s, string t) {unordered_map<char, int> need, …

【C++题解】1398. 奇偶统计

欢迎关注本专栏《C从零基础到信奥赛入门级&#xff08;CSP-J&#xff09;》 问题&#xff1a;1398. 奇偶统计 类型&#xff1a;二维数组 题目描述&#xff1a; 在一个n行m列的二维数组中&#xff0c;有若干奇数和偶数&#xff0c;请编程统计出这个二维数组中&#xff0c;奇数…

使用canal.deployer-1.1.7和canal.adapter-1.1.7实现mysql数据同步

1、下载地址 --查看是否开启bin_log日志&#xff0c;value on表示开启 SHOW VARIABLES LIKE log_bin; -- 查看bin_log日志文件 SHOW BINARY LOGS; --查看bin_log写入状态 SHOW MASTER STATUS; --查看bin_log存储格式 row SHOW VARIABLES LIKE binlog_format; --查看数据库服…

actuator字符绕过漏洞在Nginx上的配置

最近遇到了安全部门派发的actuator泄漏漏洞&#xff0c;领导希望不暴露到外网上&#xff0c;对于内网需要认证才可以访问。 要想不暴露到外网上&#xff0c;就需要在网络层面做拦截&#xff0c;比如nginx和apisix上做代理配置。 URI字符绕过的风险背景知识: URI字符绕过是一种安…

【笔记】第二节 熔炼、轧制、热处理和焊接工艺

文章目录 2.1 钢轨冶炼工艺2.1.1 冶炼工艺(1)铁水预处理(2)转炉合金化冶炼(3)钢包精炼工艺&#xff08;LF&#xff08;Ladle Furnace&#xff09;炉&#xff09; 2.1.2 技术要点(1) LF精炼(2) 夹杂物及有害元素控制非金属夹杂物P和S杂质气体 (3) 铸造组织控制钢轨材质的特点铸造…

雷尼绍圆光栅差分ABZ测量问题

雷尼绍圆光栅差分ABZ测量问题 文章目录 雷尼绍圆光栅差分ABZ测量问题引言一 设备1.1 雷尼绍圆光栅1.2 永磁同步电机1.3 M新动力驱动控制器 二 问题2.1 关于圆光栅2.1.1 电机静止时存在位置抖动问题2.1.2 脉冲数计算问题 引言 最近在调试FOC控制&#xff0c;位置反馈采用的是雷…

基于Ubuntu2404搭建mysql8配置远程访问

使用系统为Ubuntu2404&#xff0c;mysql8版本为8.0.36 安装mysql apt install -y mysql-server设置开机自启动 systemctl enable --now mysql修改密码&#xff0c;似乎是bug&#xff0c;修改密码第一次不成功&#xff0c;第二次可以 mysql use mysql; update user set Host…

局域网UDP通信实验

环境&#xff1a; 一个随身WIFI 一台笔记本电脑 一部手机 随身WIFI连接电脑 手机连接WIFI 此时手机和电脑在同一局域网中 手机IPV4地址&#xff1a;192.168.0.20 电脑IPV4地址&#xff1a;192.168.0.39 电脑端使用两台windows系统计算机简单TCP通信测试_两台计算机tcp通信-CSDN…

性能测试:Locust使用介绍(五)

事件钩子 Locust附带了许多事件钩子&#xff0c;可用于以不同的方式扩展Locust。 例如&#xff0c;以下是如何设置一个事件监听器&#xff0c;该监听器将在请求完成后触发&#xff1a; from locust import eventsevents.request.add_listener def my_request_handler(reques…

通信工程学习:什么是EDFA掺铒光纤放大器

EDFA&#xff1a;掺铒光纤放大器 EDFA&#xff0c;即掺铒光纤放大器&#xff08;Erbium-Doped Fiber Amplifier&#xff09;&#xff0c;是一种在光纤通信中广泛使用的光放大器件。以下是对EDFA的详细解释&#xff1a; 一、定义与基本原理 EDFA是在石英光纤中掺入少量的稀土元…

fpga系列 HDL:全连接层实现单个神经元PE(组成:FADD+FM)+vivado单模块仿真

vivado单模块仿真 右键模块的tb.v文件-》“Set as Top” -》点击左侧的“Run Simulation” vivado单模块综合 也可将其他模块暂时Disable PE模块单独综合的结果: 单个神经元PE的结构 processingElement.v 该模块计算两个浮点数的乘积&#xff0c;并将乘积与当前结果相加。最…

Django笔记一:搭建Django环境与URL路径访问

博主之前学从Java后端开发&#xff0c;后面获取到读研资格&#xff0c;想着未来转算法岗&#xff0c;初学Python&#xff0c;发现Python还挺有趣的&#xff0c;由于之前所学后端缘故&#xff0c;有点后端情节&#xff0c;想学习一下Django框架&#xff08;python的web框架&…

Unix时间戳与C语言的time.h库函数

目录 Unix时间戳介绍 UTC/GMT 时间与秒计数器转换代码 time.h库函数 Unix时间戳介绍 Unix 时间戳&#xff08;Unix Timestamp&#xff09;定义为从UTC/GMT的1970年1月1日0时0分0秒开始所经过的秒数&#xff0c;不考虑闰秒 时间戳存储在一个秒计数器中&#xff0c;秒计数器…

Ajax 揭秘:异步 Web 交互的艺术

Ajax 揭秘&#xff1a;异步 Web 交互的艺术 一 . Ajax 的概述1.1 什么是 Ajax ?1.2 同步和异步的区别1.3 Ajax 的应用场景1.3.1 注册表单的用户名异步校验1.3.2 内容自动补全 二 . Ajax 的交互模型和传统交互模型的区别三 . Ajax 异步请求 axios3.1 axios 介绍3.1.1 使用步骤3…

车辆重识别(关于卷积神经网络一些资料)2024/9/11

关于卷积神经网络的介绍 一&#xff0c;全连接神经网络 1&#xff0c;全连接神经网络的整体结构 X代表左边输入的数据&#xff08;向量或者矩阵等等&#xff09;&#xff0c;Y代表模型对数据处理之后的结果&#xff0c;中间的节点都可以算作为隐藏层。 2&#xff0c;全连接神经…

【C++二分查找 容斥原理】1201. 丑数 III

本文涉及的基础知识点 C二分查找 容斥原理&#xff1a;组合数学汇总 LeetCode1201. 丑数 III 丑数是可以被 a 或 b 或 c 整除的 正整数 。 给你四个整数&#xff1a;n 、a 、b 、c &#xff0c;请你设计一个算法来找出第 n 个丑数。 示例 1&#xff1a; 输入&#xff1a;n …

读构建可扩展分布式系统:方法与实践03分布式系统要点

1. 通信基础 1.1. 每个分布式系统都包含通过网络进行通信的软件组件 1.2. 硬件 1.2.1. 全球互联网就是一台异构机器&#xff0c;由不同类型的网络通信通道和设备组成&#xff0c;它们每秒将数百万条消息通过网络传送到预定目的地 1.2.2. 对于单个光纤链路&#xff0c;可以提…