Redis为什么会这么快?Redis到底有多快?

news2024/11/23 21:24:07

文章目录

  • 一、redis到底有多快?
  • 二、redis为什么这么快
    • 1、内存存储
      • (1)虚拟存储器(虚拟内存Virtual Memory)
      • (2)用户空间和内核空间
    • 2、单线程
      • (1)进程切换(上下文切换)
      • (2)进程的阻塞
      • (3)文件描述符 FD
    • 3、同步非阻塞IO
      • (1)传统IO数据拷贝
      • (2)Blocking IO
      • (3)IO多路复用(IO Multiplexing)
      • (4)多线程IO
      • (5)select和epoll的区别

一、redis到底有多快?

官方文档:https://redis.io/docs/management/optimization/benchmarks/

我们使用redis自带的benchmark脚本测试:

D:\Redis-x64-3.2.100>redis-benchmark -t set, lpush -n 100000 -q
====== lpush -n 100000 -q ======
  100000 requests completed in 0.89 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

99.90% <= 1 milliseconds
99.95% <= 5 milliseconds
99.96% <= 6 milliseconds
100.00% <= 6 milliseconds
111856.82 requests per second

我们发现,每秒可以执行11万多次set、lpush命令。

D:\Redis-x64-3.2.100>redis-benchmark -n 100000 -q script load "redis.call('set', 'lua','666')"
script load redis.call('set', 'lua','666'): 105485.23 requests per second

执行Lua脚本也能达到每秒10万多次,按照这个测试结果,redis的10万qps还是比较准确的,在高性能服务器上性能还能更强。

二、redis为什么这么快

总结起来主要是三点:
1、纯内存结构
2、请求处理单线程
3、多路复用机制

1、内存存储

KV结构的内存数据库,时间复杂度为O(1)。

(1)虚拟存储器(虚拟内存Virtual Memory)

计算机里面的内存我们叫做主内存,硬盘叫做辅存。

主存可看做一个很长的数组,一个字节一个单元,每个字节都有一个唯一的地址,这个地址叫做物理地址(Physical Address)。

早期的计算机中,如果CPU需要内存,使用物理寻址,直接访问主存储器。
在这里插入图片描述

看起来是挺合乎情理的,但是这种方式有几个弊端:
1、一般的操作系统都是多用户多任务的,所有的进程共享主存。如果每个进程都独立占一块物理地址空间,主存很快就会被用完。我们希望在不同的时刻,不同的进程可以共用同一快物理地址空间。
2、如果所有进程都是直接访问物理内存,那么一个进程就可以修改其他进程的内存数据,导致物理地址空间被破坏,程序运行就会出现异常。

咋办呢?对于物理内存的使用,应该有一个角色来协调和指挥。
在CPU和主存之间增加一个中间层。CPU不再使用物理地址访问主存,而是访问一个虚拟地址,由这个中间层把地址转换成物理地址,最终获得数据。这个中间层叫做MMU(Memory Management Unit),内存管理单元。

具体的操作如下所示:
在这里插入图片描述

我们访问MMU就跟访问物理内存一样,所以把虚拟出来的地址叫做虚拟内存(Virtual Memory)。

在每一个进程开始创建的时候,都会分配一段虚拟地址,然后通过虚拟地址和物理地址的映射来获取真实数据,这样进程就不会直接接触到物理地址,甚至不知道自己调用的哪块物理地址的数据。

目前,大多数操作系统都使用了虚拟内存,如windows系统的虚拟内存、Linux系统的交换空间等等。Windows的虚拟内存(pagefile.sys)是磁盘空间的一部分。

在32位的系统上,虚拟地址空间大小是2 ^ 32 = 4G。在64位系统上,最大虚拟地址空间理论上是2 ^ 64 = 1024 * 1024 TB,实际上没有用到64位,因为用不到那么大的空间,而且会造成很大的系统开销。Linux一般用低48位来表示虚拟地址空间,也就是2 ^ 48=256TB。

cat /proc/cpuinfo

address sizes   : 42 bits physical, 48 bits virtual

实际的物理内存可能远远小于虚拟内存的大小。

总结:引入虚拟内存的作用:

  • 1、通过把同一块物理内存映射到不同的虚拟地址空间实现内存共享
  • 2、对物理内存进行隔离,不同的进程操作互不影响
  • 3、虚拟内存可以提供更大的地址空间,并且地址空间是连续的,使得程序编写、链接更加简单。

(2)用户空间和内核空间

Linux/GNU的虚拟内存又进一步划分成了两块:一部分是内核空间(Kernel-space),一部分是用户空间(User-space)。
在这里插入图片描述
在Linux系统中,虚拟地址布局如下:
在这里插入图片描述
这两块空间的区别是什么呢?

进程的用户空间存放的是用户程序的代码和数据,内核空间中存放的是内核代码和数据。不管是内核空间还是用户空间,它们都处于虚拟内存空间中,都是对物理地址的映射。

当进程运行在内核空间时就处于内核态,而进程运行在用户空间时就处于用户态。

进程在内核空间可以访问受保护的内存空间,也可以访问底层硬件设备。也就是可以执行任意命令,调用系统的一切资源。在用户空间只能执行简单的运算,不能直接调用系统资源,必须通过系统接口(又称system call),才能向内核发出指令。

所以,这样划分的目的是为了避免用户进程直接操作内核,保证内核安全。

top命令:
在这里插入图片描述

us表示CPU消耗在User space的时间百分比;
sy表示CPU消耗在Kernel space的时间百分比。

2、单线程

按照正常思路来讲,要实现这么高的并发性能,多线程理论上来说比单线程的性能要好很多,为什么Redis要用单线程?

这里说的单线程其实是指处理客户端的请求是单线程的,可以把它叫做主线程。从4.0版本之后,还引入了一些线程处理其他的事情,比如清理脏数据、无用连接的释放、大key的删除等等。

把处理请求的主线程设置为单线程有什么好处呢?

  • 没有创建线程、销毁线程带来的消耗
  • 避免了上下文切换导致的CPU消耗
  • 避免了线程之间带来的锁竞争问题

Redis使用单线程确实有很多好处,但是不会白白浪费了多核CPU资源吗?
官方是这样解释的:
在Redis中单线程已经够用了,CPU不是redis的瓶颈。Redis的瓶颈最有可能是机器内存或者网络带宽。既然单线程容易实现,又不需要处理线程并发的问题,那就顺理成章地采用单线程的方案了。

注意,因为请求处理是单线程的,不要在生产环境运行长命令,比如keys、flushall、flushdb,否则会导致请求被阻塞。

(1)进程切换(上下文切换)

多任务操作系统是怎么实现运行远大于CPU数量的任务个数的?当然,这些任务实际上并不是真的在同时运行,而是因为系统通过时间片分片算法,在很短的时间内,将CPU轮流分配给它们,造成多任务同时运行的错觉。

在这个交替运行的过程里面,为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,以及恢复以前挂起的某个进程的执行。这种行为被称为进程切换。

什么叫上下文(Context)?

在每个任务运行前,CPU都需要知道任务从哪里加载、又从哪里开始运行。也就是说,需要系统事先帮它设置好CPU寄存器和程序计数器(Program Counter),这个叫做CPU的上下文。

而保存下来的上下文,会存储在系统内核中,并在任务重新调度执行时再次加载进来。这样就能保证任务原来的状态不受影响,让任务看起来还是连续运行。

在切换上下文的时候,需要完成一系列的工作,这是一个很消耗资源的操作。

(2)进程的阻塞

正在运行的进程由于提出系统服务请求(如IO操作),但因为某种原因未得到操作系统的立即响应,该进程只能把自己变成阻塞状态,等待相应的事件出现后才被唤醒。

进程在阻塞状态不占用CPU资源。

(3)文件描述符 FD

Linux系统将所有设备都当做文件来处理,而Linux用文件描述符来标识每个文件对象。

文件描述符(File Descriptor)是内核为了高效管理已被打开的文件所创建的索引,用于指向被打开的文件,所有执行IO操作的系统调用都通过文件描述符。

文件描述符是一个简单的非负整数,用以表明每个被进程打开的文件。Linux系统里面有三个标准文件描述符:0,标准输入(键盘);1,标准输出(显示器);2,标准错误输出(显示器)。

3、同步非阻塞IO

Redis使用了同步非阻塞IO,多路复用机制处理并发连接。

(1)传统IO数据拷贝

以读操作为例:
当应用程序执行read系统调用读取文件描述符(FD)的时候,如果这块数据已经存在于用户进程的页内存中,就直接从内存中读取数据。如果数据不存在,则先将数据从磁盘加载数据到内核缓冲区中,再从内核缓冲区拷贝到用户进程的页内存中。(两次拷贝,两次user和kernel的上下文切换)。

在这里插入图片描述
IO阻塞到底阻塞在哪里?一目了然。

(2)Blocking IO

当使用read或write对某个文件描述符进行过读写时,如果当前FD不可读,系统就不会对其他的操作做出响应。从硬件设备复制数据到内核缓冲区是阻塞的,从内核缓冲区拷贝到用户空间,也是阻塞的,直到copy complete,内核返回结果,用户进程才解除block的状态。

在这里插入图片描述
为了解决阻塞的问题,我们有几个思路:
1、在服务端创建多个线程或者使用线程池——但是在高并发的情况下需要的线程会很多,系统无法承受,而且创建和释放线程都需要消耗资源。
2、由请求方定期轮询,在数据准备完毕后再从内核缓存缓冲期复制数据到用户空间(非阻塞式IO),这种方式会存在一定的延迟。

(3)IO多路复用(IO Multiplexing)

能不能用一个线程处理多个客户端请求?答案是肯定的。

IO指的就是网络IO,多路指的多个TCP连接(Socket或Channel),复用指的是复用一个或多个线程。

它的基本原理就是不再由应用程序自己监视连接,而是由内核替应用程序监视文件描述符。

客户端在操作的时候,会产生具有不同事件类型的socket。在服务端,IO多路复用程序(IO Multiplexing Module)会把消息放入队列中,然后通过文件事件分派器(File event Dispatcher),转发到不同的事件处理器中。
在这里插入图片描述
多路复用有很多的实现,以select为例,当用户进程调用了多路复用器,进程会被阻塞。内核会监视多路复用器负责的所有socket,当任何一个socket的数据准备好了,多路复用器就会返回。这时候用户进程再调用read操作,把数据从内核缓冲期拷贝到用户空间。
在这里插入图片描述
所以,IO多路复用的特点是通过一种机制让一个进程能同时等待多个文件描述符,而这些文件描述符其中的任意一个进入读就绪(readable)状态,select()函数就可以返回。

多路复用需要操作系统的支持。Redis的多路复用,提供了select,epoll,evport,kqueue几种选择,在编译的时候来选择一种。源码ae.c:

#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else 
	#ifdef HAVE_EPOLL
	#include "ae_epoll.c"
	#else
		#ifdef HAVE_KQUEUE
		#include "ae_kqueue.c"
		#else
		#include "ae_select.c"
		#endif
	#endif
#endif

evport是Solaris系统内核提供支持的;
epoll是Linux系统内核提供支持的;
kqueue是Mac系统提供支持的;
select是POSIX提供支持的,一般的操作系统都有支撑(保底方案);
源码:ae_evport.c、ae_epoll.c、ae_kqueue.c、ae_select.c

总结一下:
Redis抽象了一套AE事件模型,将IO事件和时间时间融入一起,同时借助多路复用机制的回调特性(Linux上用epoll),似的IO读写都是非阻塞的,实现高性能的网络处理能力。

我们一直在说的Redis新版本多线程的 特性,意思并不是服务端接收客户端请求变成多线程的了,它还是单线程的。

严格意义上来说,Redis从4.0之后就引入了多线程用来处理一些耗时长的工作和后台工作,那不然的话,如果真的只有一个线程,那些耗时的操作肯定会导致客户端请求被阻塞。我们这里说的多线程,确切的说,叫做多线程IO。

(4)多线程IO

回到多路复用的图,服务端的数据返回给客户端,需要从内核空间copy数据到用户空间,然后回写到socket(write调用),这个过程使非常耗时的。所以多线程IO指的就是把结果写到socket的这个环境是多线程的。处理请求依然是单线程的,所以不存在线程并发安全问题。

(5)select和epoll的区别

select:进程可以通过把一个或者多个 fd 传递给 select 系统调用,进程会阻塞在 select 操作上,这样 select 可以帮我们检测多个 fd 是否处于就绪状态。

这个模式有二个缺点
1.由于他能够同时监听多个文件描述符,假如说有 1000 个,这个时候如果其中一个 fd 处于就绪状态了,那么当前进程需要线性轮询所有的 fd,也就是监听的 fd 越多,性能开销越大。
2.同时,select 在单个进程中能打开的 fd 是有限制的,默认是 1024,对于那些需要支持单机上万的 TCP 连接来说确实有点少。
在这里插入图片描述

epoll:linux 还提供了 epoll 的系统调用,epoll 是基于事件驱动方式来代替顺序扫描,因此性能相对来说更高,主要原理是,当被监听的 fd 中,有 fd 就绪时,会告知当前进程具体哪一个 fd 就绪,那么当前进程只需要去从指定的 fd 上读取数据即可。
另外,epoll 所能支持的 fd 上线是操作系统的最大文件句柄,这个数字要远远大于 1024。

【由于 epoll 能够通过事件告知应用进程哪个 fd 是可读的,所以我们也称这种 IO 为异步非阻塞 IO,当然它是伪异步的,因为它还需要去把数据从内核同步复制到用户空间中,真正的异步非阻塞,应该是数据已经完全准备好了,我只需要从用户空间读就行】

在这里插入图片描述

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

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

相关文章

Flutter 小技巧之 3.10 全新的 MediaQuery 优化与 InheritedModel

关于 MediaQuery 我们介绍过不少&#xff0c;比如在之前的《MediaQuery 和 build 优化你不知道的秘密》里就介绍过&#xff0c;要慎重在 Scaffold 之外使用 MediaQuery.of(context) &#xff0c;这是因为 MediaQuery.of 对 BuildContext 的绑定可能会导致一些不必要的性能开销&…

vue项目仿企业微信移动端(带聊天界面)

网上比较知名的网站或者软件&#xff0c;开源代码中都有仿其前端的。比如小米商城&#xff0c;京东商城&#xff0c;仿微信聊天页面诸如此类的。所以我潜意识觉得像那种知名公司或者软件。想找它的仿前端静态代码应该很好找。结果&#xff0c;当我想找仿企业微信移动端的前端静…

springboot+vue住房分配管理系统vue

本城镇保障性住房管理系统管理员&#xff0c;用户。管理员功能有个人中心&#xff0c;用户管理&#xff0c;房屋类型管理&#xff0c;房源信息管理&#xff0c;房源申请管理&#xff0c;住房分配管理&#xff0c;留言板管理&#xff0c;系统管理等。因而具有一定的实用性。 本…

c++11 标准模板(STL)(std::bitset)(四)

定义于头文件 <bitset> template< std::size_t N > class bitset; 类模板 bitset 表示一个 N 位的固定大小序列。可以用标准逻辑运算符操作位集&#xff0c;并将它与字符串和整数相互转换。 bitset 满足可复制构造 (CopyConstructible) 及可复制赋值 (CopyAssign…

华为OD机试真题 Java 实现【矩阵稀疏扫描】【2023 B卷 100分】,附详细解题思路

一、题目描述 如果矩阵中的许多系数都为零&#xff0c;那么该矩阵就是稀疏的。对稀疏现象有兴趣是因为它的开发可以带来巨大的计算节省&#xff0c;并且在许多大的实践中都会出现矩阵稀疏的问题。 给定一个矩阵&#xff0c;现在需要逐行和逐列地扫描矩阵&#xff0c;如果某一…

使用阿里云免费Serverless函数计算产品资源安装 Stable Diffusion

喜欢用 Stable Diffusion 的小伙伴注意了&#xff01;上一篇介绍了使用谷歌的 Colab 免费资源来安装这个画图神器。 但因为资源是免费的&#xff0c;高峰时期有可能会被谷歌断掉服务的连接&#xff0c;像周末这种使用高峰&#xff0c;GPU 的资源得不到保证。 没关系&#xff0c…

WPF开发txt阅读器:需求分析和文件读写

文章目录 需求分析读取文本文件保存文本文件 需求分析 尽管现在比较主流的阅读格式已经是epub, modi之类的&#xff0c;但txt的使用范围要远比前两者广泛&#xff0c;所以做一个txt阅读器还是有必要的。 但是对于书籍阅读而言&#xff0c;纯文本不包含目录信息&#xff0c;这…

chatgpt赋能python:Python奇数偶数判断:从基础语法到优化运行

Python奇数偶数判断&#xff1a;从基础语法到优化运行 Python是一门简单易学的编程语言&#xff0c;并且在科学计算、数据处理和人工智能等领域都有广泛的应用。在Python中&#xff0c;判断一个数是奇数还是偶数是一个基础问题&#xff0c;本文将介绍Python中奇数偶数的判断方…

事务与项目跟踪软件Jira

本文软件应网友 不长到一百四誓不改名 要求折腾&#xff1b; 什么是 Jira &#xff1f; Jira 是由 Atlassian 开发的跟踪和管理任务的软件开发工具&#xff0c;您可以将它用于敏捷和瀑布项目管理&#xff0c;以跟踪错误、功能和其他工作项。您还可以将 JIRA 配置为与许多服务管…

Leetcode之二分查找

1. 二分查找 二分查找的前提条件是&#xff1a;有序数组。 二分查找的递归实现&#xff1a; class Solution: def search_recur(self,nums:List[int],low:int,high:int,target:int):if low > high:return -1mid low (high - low) // 2if nums[mid] target:return midel…

chatgpt赋能python:Python在线播放——让您轻松畅览优质视频

Python在线播放——让您轻松畅览优质视频 在数字时代&#xff0c;视频已经成为了一种不可或缺的媒体形式&#xff0c;人们在在日常生活和工作中也更多地借助视频来实现信息传递和沟通。Python在线播放是当下最受欢迎的视频播放方式之一&#xff0c;相比于下载、实时流媒体等方…

Coursera自动驾驶2.3-2.4——传感器:IMU,GNSS,LiDAR

文章目录 一、3D几何和参考系1.旋转变换2.参考系 二、惯性测量单元1.陀螺仪2.加速计 三、全球导航卫星系统&#xff08;GNSS&#xff09;四、激光雷达1.激光雷达和测量模型&#xff08;1&#xff09;激光雷达概述&#xff08;2&#xff09;测量模型 2.点云3.平面拟合4.点云匹配…

Docker 基本管理

一、Docker 概述 Docker是一个开源的应用容器引擎&#xff0c;基于go语言开发并遵守了apache2.0协议开源。 Docker是在Linux容器里运行应用的开源工具&#xff0c;是一种轻量级的“虚拟机”。 Docker的容器技术可以在一台主机上轻松为任何应用创建一个轻量级的、可移植的、自…

rimraf : 无法加载文件 C:\Program Files\nodejs\rimraf.ps1,因为在此系统上禁止运行脚本。

问题&#xff1a; rimraf 运行 rimraf node_modules 命令报错&#xff1a;&#xff08;rimraf 前端同学可以多多了解&#xff09;rimraf : 无法加载文件 C:\Program Files\nodejs\rimraf.ps1&#xff0c;因为在此系统上禁止运行脚本。有关详细信息&#xff0c;请参阅 https:/…

C#,码海拾贝(34)——求“赫申伯格矩阵”全部“特征值”的“QR方法”之C#源代码

using System; namespace Zhou.CSharp.Algorithm { /// <summary> /// 矩阵类 /// 作者&#xff1a;周长发 /// 改进&#xff1a;深度混淆 /// https://blog.csdn.net/beijinghorn /// </summary> public partial class Matrix {…

chatgpt赋能python:Python基本词汇介绍

Python基本词汇介绍 Python是一种高级编程语言&#xff0c;它有着广泛的应用&#xff0c;从软件开发到数据科学。Python的语法简单易懂&#xff0c;它被广泛认为是一种易于学习和使用的编程语言。在本文中&#xff0c;我们将介绍一些Python基本词汇&#xff0c;让您能够更好地…

chatgpt赋能python:用Python统计奇偶数的方法

用Python统计奇偶数的方法 Python作为一种广泛应用于数据分析和科学计算的编程语言&#xff0c;具有许多内置函数和库&#xff0c;可以轻松地进行奇偶数的统计。这篇文章将向您展示如何使用Python统计奇偶数&#xff0c;并提供几个常见的示例。 Python奇偶数的定义 奇数是除…

ConcurrentHashMap核心源码(JDK1.8)

一、ConcurrentHashMap的前置知识扫盲 ConcurrentHashMap的存储结构&#xff1f; 数组 链表 红黑树 二、ConcurrentHashMap的DCL操作 HashMap线程不安全&#xff0c;在并发情况下&#xff0c;或者多个线程同时操作时&#xff0c;肯定要使用ConcurrentHashMap 无论是HashM…

Ceph分布式存储 原理+架构图详解

存储基础 单机存储设备 ●DAS&#xff08;直接附加存储&#xff0c;是直接接到计算机的主板总线上去的存储&#xff09; IDE、SATA、SCSI、SAS、USB 接口的磁盘 所谓接口就是一种存储设备驱动下的磁盘设备&#xff0c;提供块级别的存储 ●NAS&#xff08;网络附加存储&#x…

2、MySQL数据库基础

目录 MySQL 连接查询 表 约束 存储引擎 事务 索引 视图&#xff08;View&#xff09; 数据库的导入导出&#xff08;DBA命令&#xff09; 数据库设计三范式 MySQL sql、DB、DBMS分别是什么&#xff1f;它们之间的关系&#xff1f; DB&#xff1a; DataBase&#xff0…