引言

Redis 是一个单线程却性能非常好的内存数据库, 主要用来作为缓存系统。 Redis 采用网络 I/O 多路复用技术来保证在多个连接时,系统的高吞吐量(TPS)。

系统吞吐量(TPS)指的是系统在单位时间内可处理的事务的数量,是用于衡量系统性能的重要指标。影响系统吞吐量的因素很多,包括并发数和系统资源(CPU、内存、系统I/O操作、外部接口)等,系统资源等这些因素可以用平均响应时间指标来衡量

五种 I/O 模型

要理解 Redis 采用网络 I/O 多路复用技术,就得先了解五种 I/O 模型,如下:

  • 阻塞 I/O 模型

    最传统的一种 I/O 模型,即在读写数据过程中会发生阻塞现象。

    当用户线程发出 I/O 请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态(block),用户线程交出 CPU。当数据就绪之后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用户线程才解除阻塞状态。

    举例如下:

    data = socket.read();

    如果数据没有就绪,用户线程就会一直阻塞在 read 方法。

  • 非阻塞 I/O 模型

    当用户线程发起一个 read 操作后,并不需要等待,而是马上就得到了一个结果。如果结果是一个 error 时,它就知道数据还没有准备好,于是它可以再次发送 read 操作。一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回。

    所以事实上,在非阻塞 I/O 模型中,用户线程需要不断地询问内核数据是否就绪,也就说非阻塞 I/O 不会交出 CPU,而会一直占用 CPU,从而导致 CPU 占用率非常高。

  • 多路复用 I/O 模型

    多路复用 I/O 模型是目前使用得比较多的 I/O 模型。

    在多路复用 I/O 模型中,会有一个线程不断去轮询多个 socket 的状态,只有当 socket 真正有读写事件时,才真正调用实际的 I/O 读写操作。因为在多路复用 I/O 模型中,只需要使用一个线程就可以管理多个 socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只有在真正有 socket 读写事件进行时,才会使用 I/O 资源,所以它大大减少了资源占用(如 CPU)。

    会思考的你也许会想到,可以采用多线程+ 阻塞 I/O 达到类似的效果,但是由于在多线程 + 阻塞 I/O 中,每个 socket对应一个线程,这样会造成很大的资源占用,并且尤其是对于长连接来说,线程的资源一直不会释放,如果后面陆续有很多连接的话,就会造成性能上的瓶颈。

    而多路复用 I/O 模式,通过一个线程就可以管理多个 socket,只有当 socket 真正有读写事件发生才会占用资源来进行实际的读写操作。因此,多路复用IO比较适合连接数比较多的情况。

    另外,多路复用 I/O 为何比非阻塞 I/O 模型的效率高是因为在非阻塞 I/O 中,不断地询问 socket 状态时通过用户线程去进行的,而在多路复用 I/O 中,轮询每个 socket 状态是内核在进行的,这个效率要比用户线程要高的多。

    值得注意的是,多路复用 I/O 模型是通过轮询的方式来检测是否有事件到达,并且对到达的事件逐一进行响应。因此对于多路复用 I/O 模型来说,一旦事件响应太慢,那么就会导致后续的事件迟迟得不到处理,并且会影响新的事件轮询。

    这就是说,如果 Redis 每条命令执行如果占用大量时间,就会造成其他线程阻塞,对于 Redis 这种高性能服务是致命的,所以 Redis 是面向高速执行的数据库。

  • 信号驱动 I/O 模型

    在信号驱动 I/O 模型中,当用户线程发起一个 I/O 请求操作,会给对应的 socket 注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号之后,便在信号函数中调用 I/O 读写操作来进行实际的 I/O 请求操作。

    这个一般用于 UDP 中,对 TCP 套接口几乎是没用的,原因是该信号产生得过于频繁,并且该信号的出现并没有告诉我们发生了什么事情。

  • 异步 I/O 模型

    异步 I/O 模型才是最理想的 I/O 模型,在异步 I/O 模型中,当用户线程发起 read 操作之后,立刻就可以开始去做其它的事。而另一方面,从内核的角度,当它收到一个 asynchronous read 之后,它会立刻返回,说明 read 请求已经成功发起了,因此不会对用户线程产生任何阻塞(block)。

    然后,内核会等待数据准备完成,然后将数据拷贝到用户线程,当这一切都完成之后,内核会给用户线程发送一个信号,告诉它 read 操作完成了。

    也就是说,用户线程完全不需要关心实际的整个 I/O 操作是如何进行的,只需要先发起一个请求,当接收内核返回的成功信号时表示 I/O 操作已经完成,可以直接去使用数据了。

这五种 I/O 模型中,前面四种 I/O 模型实际上都属于同步 I/O,只有最后一种是真正的异步 I/O,因为无论是多路复用 I/O模型还是信号驱动 I/O 模型,I/O 操作的第 2 个阶段都会引起用户线程阻塞,也就是内核进行数据拷贝的过程都会让用户线程阻塞。

为何 Redis 要使用 I/O 多路复用技术

现在来回答这个问题就 so easy 啦!

首先,Redis 是单线程架构,所有的命令操作都是先进入队列,然后一个一个按照顺序线性执行的,但是由于读写操作等待用户输入或输出都是阻塞的,所以 I/O 操作在一般情况下往往不能直接返回,这会导致某一文件的 I/O 阻塞导致整个进程无法对其它客户提供服务,而采用 I/O 多路复用技术就是为了解决这个问题。

Redis 为何不采用异步 I/O 模型,这个不是效率更高吗?这玩意儿在多线程下才能发挥功效,而 Redis 是单线程架构哈。

epoll 是什么

epoll 其实只是众多实现 I/O多路复用模型的技术当中的一种而已,但是相比其他 I/O 多路复用技术(select, poll等),epoll有诸多优点(Redis 也支持 select 和 poll,默认使用 epoll)

  1. epoll 没有最大并发连接的限制,上限是最大可以打开文件的数目,这个数字一般远大于 2048,
  2. 效率提升, epoll 最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中, epoll 的效率就会远远高于 select 和 poll
  3. 内存拷贝, epoll 直接使用的 "共享内存",可以跳过传统的内存拷贝操作,效率更高

总结

Redis 采用 I/O 多路复用技术(epoll)是因为 Redis 是单线程架构,是为了避免网络 I/O 读写操作阻塞整个进程。

Redis I/O 多路复用技术原理的更多相关文章

  1. Lind.DDD.RedisClient~对StackExchange.Redis调用者的封装及多路复用技术

    回到目录 两雄争霸 使用StackExchange.Redis的原因是因为它开源,免费,而对于商业化的ServiceStack.Redis,它将一步步被前者取代,开源将是一种趋势,商业化也值得被我们尊 ...

  2. 大数据相关技术原理资料整理(hdfs, spark, hbase, kafka, zookeeper, redis, hive, flink, k8s, OpenTSDB, InfluxDB, yarn)

    hdfs: hdfs官方文档 深入理解HDFS的架构和原理 https://blog.csdn.net/kezhong_wxl/article/details/76573901 HDFS原理解析(总体 ...

  3. Redis单机数据库的实现原理

    本文主要介绍Redis的数据库结构,Redis两种持久化的原理:RDB持久化.AOF持久化,以及Redis事件分类及执行原理.最后,分别介绍了单机班Redid客户端和Redis服务器的使用和实现原理. ...

  4. 液晶常用接口“LVDS、TTL、RSDS、TMDS”技术原理介绍

    液晶常用接口“LVDS.TTL.RSDS.TMDS”技术原理介绍 1:Lvds Low-Voltage Differential Signaling 低压差分信号 1994年由美国国家半导体公司提出之 ...

  5. Oracle体系结构之控制文件的多路复用技术

    在Windows操作系统中,如果注册表文件被损坏了,就会影响操作系统的稳定性.严重的话,会导致操作系统无法正常启动.而控制文件对于Oracle数据库来说,其作用就好象是注册表一样的重要.如果控制文件出 ...

  6. 进阶的Redis之哈希分片原理与集群实战

    前面介绍了<进阶的Redis之数据持久化RDB与AOF>和<进阶的Redis之Sentinel原理及实战>,这次来了解下Redis的集群功能,以及其中哈希分片原理. 集群分片模 ...

  7. I/O多路复用技术(multiplexing)是什么?

    作者:知乎用户链接:https://www.zhihu.com/question/28594409/answer/52763082来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...

  8. 解读I/O多路复用技术

    前言 当我们要编写一个echo服务器程序的时候,需要对用户从标准输入键入的交互命令做出响应.在这种情况下,服务器必须响应两个相互独立的I/O事件:1)网络客户端发起网络连接请求,2)用户在键盘上键入命 ...

  9. Redis | 第5章 Redis 中的持久化技术《Redis设计与实现》

    目录 前言 1. RDB 持久化 1.1 RDB 文件的创建与载入 1.2 自动间隔性保存 1.2.1 设置保存条件 1.2.2 dirty 计数器和 lastsave 属性 1.2.3 检查保存条件 ...

随机推荐

  1. Centos6无法使用yum解决办法

    12月后Centos 6 系统无法使用yum出现错误(文章底部看) 相信已经有一部分朋友今天连接到CentOS 6的服务器后执行yum后发现报错,那么发生了什么? CentOS 6已经随着2020年1 ...

  2. Chrome扩展开发基础教程(附HelloWorld)

    1 概述 Chrome扩展开发的基础教程,代码基于原生JS+H5,教程内容基于谷歌扩展开发官方文档. 2 环境 Chrome 88.0.4324.96 Chromium 87.0.4280.141 B ...

  3. .ts 视频文件 .m3u8视频文件合并 转换为mp4

    1. 先把 .m3u8.zip 格式文件解压到文件夹D:\a. 2. 将文件夹内的 .ts文件名处理好. 一般均为按顺序排列. (殊情况需要根据.m3u8文件记录处理.) 举例,根据实际情况处理. ( ...

  4. Day11_50_SortedMap集合

    SortedMap集合 二叉查找树 和 二叉*衡树 二叉查找树是一种有序的树,所有的左孩子的value值都是小于叶子结点的value值的,所有右孩子的value值都是大于叶子结点的.这样做的好处在于: ...

  5. 计算eks node 中pod数量

    计算eks node 中pod数量 计算公式:((IP数I - 1) * ENI数) + 2 实例规格等ENI数和IP的对应关系,请参考 https://docs.aws.amazon.com/zh_ ...

  6. 假如kubernetes不支持docker了该怎么办

    假如kubernetes不支持docker了该怎么办 从官网安装文档可以看到kubernetes支持一下几种: https://kubernetes.io/docs/setup/production- ...

  7. 关于Mapreduce Text类型赋值的错误

    Mapreduce中Text类型数据被无缘无故替换? ​ 今天偶然看到一个mapreduce demo,直接上手操作 统计两个文件中 最大值 文件中数据格式为 名字 数值 输出为 名字(最大值所对应的 ...

  8. C++雾中风景17:模板的非推断语境与std::type_identity

    乍一看这个标题很玄乎,但是其实这只是涉及一个很简单的CPP的模板推导的知识点. 笔者近期进行CPP开发工作时,在编译时遇到了如下的模板类型的推断错误:note: candidate template ...

  9. Unittest单元测试框架——BeautifulReport测试报告和Yagmail自动发送邮件

    一.前言 之前在做appium自动化的时候,已经提到过unittest框架的基本概念.用例执行,以及BeautifulReport测试报告的简单使用了(地址:https://www.cnblogs.c ...

  10. RF-获取body

    一个很简单的场景:GET方法获取某URL的body信息,直接读取即可,不做处理 *** Test Cases *** GetToken Create HTTP Context host=192.168 ...