Redis是单线程还是多线程?

通常我们所说的Redis 是单线程,主要是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的,这也是 Redis 对外提供键值存储服务的主要流程。但 Redis 的其他功能,比如持久化、异步删除、集群数据同步等,其实是由额外的线程执行的。所以严格来说Redis并不是单线程的。

Redis为什么不用多线程处理每个命令呢?

想必大家都听过,多线程能提高系统吞吐率这个说法了,但是这个的前提是要有很好的系统设计,尤其是共享资源的并发访问控制问题,如果没有精心的设计,那么并行也会变成串行,而且,采用多线程开发一般会引入同步原语来保护共享资源的并发访问,这也会降低系统代码的易调试性和可维护性。为了避免这些问题,Redis 直接采用了单线程模式。

Redis的多路复用机制

Redis之所以这么快除了完全基于内存计算和高效的数据结构意外,还有一个重要的原因就是采用了多路复用机制,使其在网络 IO 操作中能并发处理大量的客户端请求,实现高吞吐率。

要想了解IO模型,首先我们要知道IO操作是基于什么来实现的,如果一个应用程序, 想对外提供服务, 一般都是通过建立套接字监听端口来实现, 也就是socket,IO多路复用是系统来实现的并不是Redis实现的,这个需要系统层面的支持。不要搞混哈,不过现在很多系统都实现了IO多路复用,可能只是不同的系统实现方式不同而已。

好,那下面我们先来搞一下基本的IO模型和它的阻塞点?

以 Get 请求为例,Redis为了处理一个GET请求,先要监听客户端请求(bind/listen),和客户端建立连接(accept),从 socket 中读取请求(recv),解析客户端发送请求(parse),根据请求类型读取键值数据(get),最后给客户端返回结果,即向 socket 中写回数据(send)。下图显示了这一过程,其中,bind/listen、accept、recv、parse 和 send 属于网络 IO 处理,而 get 属于键值数据操作。既然 Redis 是单线程,那么,最基本的一种实现是在一个线程中依次执行上面说的这些操作。

但是,在这里的网络 IO 操作中,有潜在的阻塞点,分别是 accept() 和 recv()。当 Redis 监听到一个客户端有连接请求,但一直未能成功建立起连接时,会阻塞在 accept() 函数这里,导致其他客户端无法和 Redis 建立连接。类似的,当 Redis 通过 recv() 从一个客户端读取数据时,如果数据一直没有到达,Redis 也会一直阻塞在 recv()。

这就导致 Redis 整个线程阻塞,无法处理其他客户端请求,效率很低。不过,幸运的是,socket 网络模型本身支持非阻塞模式。

非阻塞模式

非阻塞模式Socket 网络模型的非阻塞模式设置,主要体现在三个关键的函数调用上,如果想要使用 socket 非阻塞模式,就必须要了解这三个函数的调用返回类型和设置模式。接下来,我们就重点学习下它们。

在 socket 模型中,不同操作调用后会返回不同的套接字类型。socket() 方法会返回主动套接字,然后调用 listen() 方法,将主动套接字转化为监听套接字,此时,可以监听来自客户端的连接请求。最后,调用 accept() 方法接收到达的客户端连接,并返回已连接套接字。


针对监听套接字,我们可以设置非阻塞模式:当 Redis 调用 accept() 但一直未有连接请求到达时,Redis 线程可以返回处理其他操作,而不用一直等待。但是,你要注意的是,调用 accept() 时,已经存在监听套接字了。

虽然 Redis 线程可以不用继续等待,但是总得有机制继续在监听套接字上等待后续连接请求,并在有请求时通知 Redis。

类似的,我们也可以针对已连接套接字设置非阻塞模式:Redis 调用 recv() 后,如果已连接套接字上一直没有数据到达,Redis 线程同样可以返回处理其他操作。我们也需要有机制继续监听该已连接套接字,并在有数据达到时通知 Redis。

这样才能保证 Redis 线程,既不会像基本 IO 模型中一直在阻塞点等待,也不会导致 Redis 无法处理实际到达的连接请求或数据。

到此,Linux 中的 IO 多路复用机制就要登场了,上面说了多路复用机制是系统层面去实现的哦。

基于多路复用的高性能 I/O 模型

Linux 中的 IO 多路复用机制是指一个线程处理多个 IO 流,就是我们经常听到的 select/epoll 机制。简单来说,在 Redis 只运行单线程的情况下,该机制允许内核中,同时存在多个监听套接字和已连接套接字。内核会一直监听这些套接字上的连接请求或数据请求。一旦有请求到达,就会交给 Redis 线程处理,这就实现了一个 Redis 线程处理多个 IO 流的效果。

下图就是基于多路复用的 Redis IO 模型。图中的多个 FD 就是刚才所说的多个套接字。Redis 网络框架调用 epoll 机制,让内核监听这些套接字。此时,Redis 线程不会阻塞在某一个特定的监听或已连接套接字上,也就是说,不会阻塞在某一个特定的客户端请求处理上。正因为此,Redis 可以同时和多个客户端连接并处理请求,从而提升并发性。

为了在请求到达时能通知到 Redis 线程,select/epoll 提供了基于事件的回调机制,即针对不同事件的发生,调用相应的处理函数。

那么,回调机制是怎么工作的呢?其实,select/epoll 一旦监测到 FD 上有请求到达时,就会触发相应的事件,其实就是Redis会在select/epoll 机制上提前去注册Redis自己提供的回调函数。

这些事件会被放进一个事件队列,Redis 单线程对该事件队列不断进行处理。这样一来,Redis 无需一直轮询是否有请求实际发生,这就可以避免造成 CPU 资源浪费。同时,Redis 在对事件队列中的事件进行处理时,会调用相应的处理函数,这就实现了基于事件的回调。因为 Redis 一直在对事件队列进行处理,所以能及时响应客户端请求,提升 Redis 的响应性能。

为了方便你理解,我再以连接请求和读数据请求为例,具体解释一下。

这两个请求分别对应 Accept 事件和 Read 事件,Redis 分别对这两个事件注册 accept 和 get 回调函数。当 Linux 内核监听到有连接请求或读数据请求时,就会触发 Accept 事件和 Read 事件,此时,内核就会回调 Redis 相应的 accept 和 get 函数进行处理。

另外Redis在6.0推出了多线程,可以在高并发场景下利用CPU多核多线程读写客户端数据,进一步提升server性能,当然,只是针对客户端的读写是并行的,每个命令的真正操作依旧是单线程的。

Redis的多路复用机制相关推荐

  1. Redis IO 多路复用机制

    Redis IO 多路复用机制 基于linux select/epoll select:最大支持1024个文件描述符,在描述符较多情况下性能较差,水平触发 poll:poll与select基本相同,只 ...

  2. Redis进阶-事件机制

    1. 事件机制 Redis 中的事件驱动库,它只会去关注网络 I/O事件,以及定时器事件. 所以,Redis 的事件库处理下面两类事件: 文件事件(file event):用于处理 Redis 服务器 ...

  3. Redis非阻塞I/O多路复用机制

    小曲在S城开了一家快递店,负责同城快送服务.小曲因为资金限制,雇佣了一批快递员,然后小曲发现资金不够了,只够买一辆车送快递. 经营方式一 客户每送来一份快递,小曲就让一个快递员盯着,然后快递员开车去送 ...

  4. 多路复用机制--Redis为什么这么快

    IO 多路复用机制,核心思想是让单个线程去监视多个连接,一旦某个连接就绪, 也就是触发了读/写事件. 就通知应用程序,去获取这个就绪的连接进行读写操作. 也就是在应用程序里面可以使用单个线程同时处理多 ...

  5. 非阻塞I/O多路复用机制

    题外话:我们现在要仔细的说一说I/O多路复用机制,因为这个说法实在是太通俗了,通俗到一般人都不懂是什么意思.博主打一个比方:小曲在S城开了一家快递店,负责同城快送服务.小曲因为资金限制,雇佣了一批快递 ...

  6. Redis原理和机制详解

    什么是Redis? Redis 是开源免费的,遵守BSD协议,是一个高性能的key-value非关系型数据库. Redis特点: Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候 ...

  7. 让人秒懂的Redis的事件处理机制

    redis是单进程,单线程模型,与nginx的多进程不同,与golang的多协程也不同,"工作的工人"那么少,可那么为什么redis能这么快呢? epoll多路复用 这里重点要说的 ...

  8. Redis IO多路复用理解

    IO多路复用在Redis中的应用 Redis 服务器是一个事件驱动程序, 服务器处理的事件分为时间事件和文件事件两类. 文件事件:Redis主进程中,主要处理客户端的连接请求与相应. 时间事件:for ...

  9. Redis底层多路复用

    Redis6:第十二篇-多路复用相关问题 Redis的多路复用 什么是IO多路复用 文本事件 同步异步阻塞非阻塞 同步 异步 阻塞 非阻塞 四种组合方式 Unix操作系统底层的五种最重要的IO模型 B ...

最新文章

  1. 教你在64位Win7系统下使用ObRegisterCallbacks内核函数来实现进程保护
  2. 2021年软考考试时间公布
  3. matlab var std,Matlab var std cov 函数解析
  4. java调用solr的分词查询结果
  5. QT4.7和VS2008 顺利安装必读 (最新版)
  6. python定义类的程序_python扫码签到程序python中如何定义类
  7. 用C#制作PDF文件全攻略
  8. Inspinia_admin-V2.3原版(英文)
  9. 牛客小白月赛2 J 美 【构造】
  10. 使用formData对象提交表单并上传图片
  11. js删除某个节点之后的所有节点
  12. 双十一不孤单,再过几小时北欧人民也和你一样开始抢单
  13. 成都市二手房行情分析
  14. Python判断素数(质数)——循换结构、控制及else循环扩展模式的实践
  15. AngularJS中的$resource使用与Restful资源交互
  16. USACO 2.1.4 健康的荷斯坦奶牛 Healthy Holsteins
  17. pkav之当php懈垢windows通用上传缺陷
  18. 搭建gitserver并实现git push 自动部署
  19. leetCode1047
  20. 数据处理(1):十万加SHP数据重载导入标准字段图层

热门文章

  1. 大数据专业学校课程安排 (仅供参考)
  2. 浏览器html5效果测试,8款浏览器对HTML5的支持测试
  3. 【个人亲测】2018最流行的浏览器排行榜前10
  4. stata面板数据聚类及数据导入处理、虚拟变量等
  5. 【转载】怎么设置博客背景
  6. PD芯片程序烧录方法
  7. android可拖拽九宫格,微信小程序实现九宫格图片拖拽
  8. 微信小程序小型按钮内文字布局变形
  9. Java集合中Set
  10. Non-static method ‘selectUser(com.lsc.bean.admin)‘ cannot be referenced from a static context