代码编织梦想

最近在看Redis的IO线程模型,看到了这篇文章讲的比较好也通俗易懂,所以转载下来,方便以后再看。

以下是原文的正文部分:

原文地址: 一文搞懂 Redis高性能之IO多路复用

一文搞懂 Redis高性能之IO多路复用

相信大家在面试过程中经常会被问到:“单线程的 Redis 为啥这么快?

哈哈,反正我在面试时候经常会问候选人这个问题,这个问题其实是对 redis 内部机制的一个考察,可以牵扯出好多涉及底层深入原理的一些列问题。

回到问题本身,基本的回答就两点:

  • 完全基于内存

  • IO 多路复用

1、关于第 1 点比较好理解。Redis 绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于 HashMap,查找和操作的时间复杂度都是 O(1)。

2、关于第 2 点 IO 多路复用,有些同学看到概念后感觉一头雾水,到底什么是 IO 多路复用?

本文从 IO 并发性能提升来整体思考,来逐步剖析 IO 多路复用的原理。

一、如何快速理解 IO 多路复用?

  • 多进程

  • 多线程

  • 基于单进程的 IO 多路复用(select/poll/epoll)

1、多进程

对于并发情况,假如一个进程不行,那搞多个进程不就可以同时处理多个客户端连接了么?

多进程这种方式的确可以解决了服务器在同一时间能处理多个客户端连接请求的问题,但是仍存在一些缺点:

  • fork()等系统调用会使得进程上下文进行切换,效率较低

  • 进程创建的数量随着连接请求的增加而增加。比如 10w 个请求,就要 fork 10w 个进程,开销太大

  • 进程与进程之间的地址空间是私有、独立的,使得进程之间的数据共享变得困难

2、多线程

线程是运行在进程上下文的逻辑流,一个进程可以包含多个线程,多个线程运行在同一进程上下文中,因此可共享这个进程地址空间的所有内容,解决了进程与进程之间通信难的问题。

同时,由于一个线程的上下文要比一个进程的上下文小得多,所以线程的上下文切换,要比进程的上下文切换效率高得多。

3、IO 多路复用

简单理解就是:一个服务端进程可以同时处理多个套接字描述符。

  • 多路:多个客户端连接(连接就是套接字描述符)

  • 复用:使用单进程就能够实现同时处理多个客户端的连接

以上是通过增加进程和线程的数量来并发处理多个套接字,免不了上下文切换的开销,而 IO 多路复用只需要一个进程就能够处理多个套接字,从而解决了上下文切换的问题。

其发展可以分 select->poll→epoll 三个阶段来描述。

二、如何简单理解 select/poll/epoll 呢?

按照以往惯例,还是联系一下我们日常中的现实场景,这样更助于大家理解。

举栗说明:

领导分配员工开发任务,有些员工还没完成。如果领导要每个员工的工作都要验收 check,那在未完成的员工那里,只能阻塞等待,等待他完成之后,再去 check 下一位员工的任务,造成性能问题。

那如何解决这个问题呢?

1、select

举栗说明:

领导找个 Team Leader(后文简称 TL),负责代自己 check 每位员工的开发任务。

TL 的做法是:遍历问各个员工“完成了么?”,完成的待 CR check 无误后合并到 Git 分支,对于其他未完成的,休息一会儿后再去遍历....

这样存在什么问题呢?

  • 这个 TL 存在能力短板问题,最多只能管理 1024 个员工

  • 很多员工的任务没有完成,而且短时间内也完不成的话,TL 还是会不停的去遍历问询,影响效率。

select 函数:

int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout);

复制代码

select 函数监视的文件描述符分 3 类,分别是 writefds、readfds、和 exceptfds。调用后 select 函数会阻塞,直到有描述符就绪(有数据可读、可写、或者有 except),或者超时(timeout 指定等待时间,如果立即返回设为 null 即可),函数返回。当 select 函数返回后,可以通过遍历 fdset,来找到就绪的描述符。

select 具有良好的跨平台支持,其缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在 Linux 上一般为 1024。

2、poll

举栗说明:

换一个能力更强的 New Team Leader(后文简称 NTL),可以管理更多的员工,这个 NTL 可以理解为 poll。

poll 函数:

intpoll(structpollfd*fds, nfds_t nfds,int timeout);
typedef struct pollfd{ 
int fd; // 需要被检测或选择的文件描述符 
short events; // 对文件描述符fd上感兴趣的事件 
short revents; // 文件描述符fd上当前实际发生的事件
} pollfd_t;

复制代码

poll 改变了文件描述符集合的描述方式,使用了 pollfd 结构而不是 select 的 fd_set 结构,使得 poll 支持的文件描述符集合限制远大于 select 的 1024。

3、epoll

举栗说明:

在上一步 poll 方式的 NTL 基础上,改进一下 NTL 的办事方法:遍历一次所有员工,如果任务没有完成,告诉员工待完成之后,其应该做 xx 操作(制定一些列的流程规范)。这样 NTL 只需要定期 check 指定的关键节点就好了。这就是 epoll。

Linux 中提供的 epoll 相关函数如下:

intepoll_create(int size);intepoll_ctl(int epfd,int op,int fd,struct epoll_event *event);intepoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout);

复制代码

epoll 是 Linux 内核为处理大批量文件描述符而作了改进的 poll,是 Linux 下多路复用 IO 接口 select/poll 的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统 CPU 利用率。

4、小结

  • select 就是轮询,在 Linux 上限制个数一般为 1024 个

  • poll 解决了 select 的个数限制,但是依然是轮询

  • epoll 解决了个数的限制,同时解决了轮询的方式

三、IO 多路复用在 Redis 中的应用

Redis 服务器是一个事件驱动程序, 服务器处理的事件分为时间事件和文件事件两类。

  • 文件事件:Redis 主进程中,主要处理客户端的连接请求与相应。

  • 时间事件:fork 出的子进程中,处理如 AOF 持久化任务等。

由于 Redis 的文件事件是单进程,单线程模型,但是确保持着优秀的吞吐量,IO 多路复用起到了主要作用。

文件事件是对套接字操作的抽象,每当一个套接字准备好执行连接应答、写入、读取、关闭等操作时,就会产生一个文件事件。因为一个服务器通常会连接多个套接字,所以多个文件事件有可能会并发地出现。

IO 多路复用程序负责监听多个套接字并向文件事件分派器传送那些产生了事件的套接字。文件事件分派器接收 IO 多路复用程序传来的套接字,并根据套接字产生的事件的类型,调用相应的事件处理器。示例如图所示:

文件处理器的四个组成部分

Redis 的 IO 多路复用程序的所有功能都是通过包装常见的 select、poll、evport 和 kqueue 这些 IO 多路复用函数库来实现的,每个 IO 多路复用函数库在 Redis 源码中都有对应的一个单独的文件。

Redis 为每个 IO 多路复用函数库都实现了相同的 API,所以 IO 多路复用程序的底层实现是可以互换的。如图:

多个IO复用库实现可选

Redis 把所有连接与读写事件、还有我们没提到的时间事件一起集中管理,并对底层 IO 多路复用机制进行了封装,最终实现了单进程能够处理多个连接以及读写事件。这就是 IO 多路复用在 redis 中的应用。

四、总结

Redis 6.0 之后的版本开始选择性使用多线程模型。

Redis 选择使用单线程模型处理客户端的请求主要还是因为 CPU 不是 Redis 服务器的瓶颈,使用多线程模型带来的性能提升并不能抵消它带来的开发成本和维护成本,系统的性能瓶颈也主要在网络 I/O 操作上;

而 Redis 引入多线程操作也是出于性能上的考虑,对于一些大键值对的删除操作,通过多线程非阻塞地释放内存空间也能减少对 Redis 主线程阻塞的时间,提高执行的效率。

12.redis的事件驱动(io多路复用)_郑学炜的博客-爱代码爱编程

目录 §  从Redis的工作模式谈起 §  Reactor模式 ·        C10K问题 ·        I/O多路复用技术 ·        Reactor的定义 ·        Java中的NIO与Netty §  Redis与Reactor §  总结 §  参考资料 从Redis的工作模式谈起 我们在使用Redis的时候,通常是多个客户

io多路复用—由redis的io多路复用yinch_happywuw的博客-爱代码爱编程

linux IO多路复用有epoll, poll, select,epoll性能比其他几者要好。   名词比较绕口,理解涵义就好。一个epoll场景:一个酒吧服务员(一个线程),前面趴了一群醉汉,突然一个吼一声“倒酒”(事件),你小跑过去给他倒一杯,然后随他去吧,突然又一个要倒酒,你又过去倒上,就这样一个服务员服务好多人,有时没人喝酒,服务员处于空闲状

redis io多路复用技术以及epoll实现原理_wxy941011的博客-爱代码爱编程_redis多路复用原理

redis 是一个单线程却性能非常好的内存数据库, 主要用来作为缓存系统。 redis 采用网络IO多路复用技术来保证在多连接的时候, 系统的高吞吐量。 为什么 Redis 中要使用 I/O 多路复用这种技术呢? 首先,Redis 是跑在单线程中的,所有的操作都是按照顺序线性执行的,但是由于读写操作等待用户输入或输出都是阻塞的,所以 I/O

redis之io线程、io多路复用,bio、nio和aio区别-爱代码爱编程

参考文章:https://blog.csdn.net/Happy_wu/article/details/80052617 redis的线程 redis是单线程操作的,但是却可以处理高并发。原因是基于多路复用的非阻塞IO,

io多路复用三种机制select,poll,epoll-爱代码爱编程

        I/O多路复用(multiplexing)的本质是通过一种机制(系统内核缓冲I/O数据),让单个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作。 select、poll 和 epoll 都是 Linux API 提供的 IO 复用方式。 相信大家都了解了Unix五种IO模型,不了解

Redis IO多路复用技术-爱代码爱编程

转:https://www.cnblogs.com/syyong/p/6231326.html Redis采用的是基于内存的采用的是单进程单线程模型的KV数据库,由C语言编写。官方提供的数据是可以达到100000+的qps。这个数据不比采用单进程多线程的同样基于内存的KV数据库Memcached差。 Redis快的主要原因是: 完全基于内存数据结构简

redis原理,为什么快,io多路复用-爱代码爱编程

很多同学对Redis的单线程和I/O多路复用技术并不是很了解,所以我用简单易懂的语言让大家了解下Redis单线程和I/O多路复用技术的原理,对学好和运用好Redis打下基础。 一、Redis的单线程理解 Redis客户端对服务端的每次调用都经历了发送命令,执行命令,返回结果三个过程。其中执行命令阶段,由于Redis是单线程来处理命令的,所有到达服务端的

Redis系列--IO多路复用-爱代码爱编程

Redis Server跑在单进程单线程中,接收到的命令操作都是按照顺序线性执行的,即便如此,它的读写性能依然能达到10W+的QPS,不得不说:Redis的设计十分优秀。 为什么Redis的读写性能这么高呢?原因有许多,我们列举主要的三个:     1、Redis基于内存操作: 绝大部分的请求为纯粹的内存操作,而且使用hash结构存储数据,查找和操作

Redis-IO多路复用-爱代码爱编程

1、“I/O多路复用”这个坑爹翻译可能是这个概念在中文里面如此难理解的原因; 2、I/O多路复用,即:I/O multiplexing,这里面的 multiplexing 指的其实是在单个线程通过记录跟踪每一个Sock(I/O流)的状态来同时管理多个I/O流。发明它的原因,是尽量多的提高服务器的吞吐能力; 3、可以理解成是一种 “时分复用”;

Redis(一):基础知识以及IO多路复用模型-爱代码爱编程

一:Redis简介   简单来说 redis 就是一个数据库,不过与传统数据库不同的是 redis 的数据是存在内存中的,所以读写速度非常快,因此 redis 被广泛应用于缓存方向。另外,redis 也经常用来做分布式锁。redis 提供了多种数据类型来支持不同的业务场景。除此之外,redis 支持事务 、持久化、LUA脚本、LRU驱动事件、多种集群方

redis教程-爱代码爱编程

备注:个人学习笔记,是学习B站上的狂神说Java的课程摘录的笔记。 视频地址:https://www.bilibili.com/video/BV1S54y1R7SB B站up主:狂神说Java 笔记参考来源:微信公众号:狂神说 Nosql概述 NoSQL,泛指非关系型的数据库。随着互联网web2.0网站的兴起,传统的关系数据库在处理web2.0网站,特

带你深入了解IO多路复用技术-爱代码爱编程

IO多路复用技术是操作系统级的技术,也就是我们常说的底层原理。好多框架,中间件都是使用了IO多路复用技术,才使其具备更高的性能,比如我们经常使用的Redis、Nginx和我们耳熟能详的高性能通信框架Netty。本篇文章我们一起探究一下IO多路复用技术。 1 IO的理解 I - Input O - output 这里的IO我们常常指网络的IO