我们经常听到说Redis是单线程的,也会有疑问:为什么单线程的Redis能那么快?

这里要明白一点:Redis是单线程,主要是指Redis的网络IO和键值对读写是由一个线程来完成的,这也是Redis对外提供键值存储服务的主要流程。但Redis的其他功能,比如持久化、异步删除、集群数据同步等,都是由额外的线程执行的。

我们知道多线程能够提升并发性能,那为什么Redis会采用单线程,而非多线程?为什么单线程能那么快?

下面我们就来学习一下Redis采用单线程的原因。

为什么采用单线程?

使用多线程,虽然可以增加系统吞吐率,或是增加系统扩展性,但同样会产生开销。

Redis的数据是在内存里的,是共享的,如果使用多线程就会引发共享资源的竞争,需要引入互斥锁来解决,使得并行变串行。最终系统吞吐率并没有随着线程的增加而增加。

另外,多线程开发需要精细的设计,会增加系统的复杂度,降低代码的易调试性和可维护性。为了避免这些问题,Redis采用单线程模式。

单线程Redis为什么那么快?

通常来说,单线程的处理能力比多线程要差很多,那Redis却能使用单线程模型达到每秒数十万级别的处理能力,这是为什么呢?

一方面,Redis大多数操作是在内存上完成的,并且采用高效的数据结构,例如哈希表和跳表。另一方面,Redis采用了多路复用机制,使其在网络IO操作中能并发处理大量的客户端请求,实现高吞吐率。

在学习多路复用机制前,我们要弄明白网络操作的基于IO模型和潜在的阻塞点。

基本IO模型与阻塞点

以Get请求为例,为了处理一个Get请求:

  1. 需要监听客户端请求(bind/listen)
  2. 和客户端建立连接(accept)
  3. 从socket中读取请求(recv)
  4. 解析客户端发送请求(parse)
  5. 根据请求类型读取键值数据(get)
  6. 最后给客户端返回结果,即向socket中写回数据(send)。

下图显示了这一过程,其中,bind/listen、accept、recv、parse和send属于网络IO处理,而get属性键值数据操作。

但是在这里的网络IO操作中,有潜在的阻塞点,分别是accept()和recv()。

  • 当Redis监听到一个客户端有连接请求,但一直未能成功建立起连接时,会阻塞在accept()
  • 当Redis通过recv()从一个客户端读取数据时,如果数据一直没有到达,Redis也会一直阻塞在recv()

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

非阻塞模式

Socket网络模型可以设置非阻塞模式。

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

下面就到多路复用机制登场了。

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

Linux的IO多路复用机制是指一个线程处理多个IO流,也就是select/epoll机制。

在Redis运行单线程下,该机制允许内核中,同时存在多个监听套接字和已连接套接字。

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

回调机制的工作流程:

  1. select/epoll一旦临听到FD上有请求到达,就会触发相应的事件,并放进一个事件队列中。
  2. Redis单线程对事件队列进行处理即可,无需一直轮询是否有请求发生,避免CPU资源浪费。

因为Redis一直在对事件队列进行处理,所以能及时响应客户端请求,提升Redis的响应性能。

不过,需要注意的是,在不同的操作系统上,多路复用机制也是适用的。

拓展

在“Redis基本IO模型”图中,有哪些潜在的性能瓶颈?

Redis单线程处理IO请求性能瓶颈主要包括2个方面:

1、任意一个请求在server中一旦发生耗时,都会影响整个server的性能 也就是说后面的请求都要等前面这个耗时请求处理完成,自己才能被处理到。

耗时的操作包括:

  • 操作bigkey:写入一个bigkey在分配内存时需要消耗更多的时间,同样,删除bigkey释放内存同样会产生耗时
  • 使用复杂度过高的命令:例如SORT/SUNION/ZUNIONSTORE,或者O(N)命令,但是N很大,例如lrange key 0 -1一次查询全量数据
  • 大量key集中过期:Redis的过期机制也是在主线程中执行的,大量key集中过期会导致处理一个请求时,耗时都在删除过期key,耗时变长
  • 淘汰策略:溜达策略也是在主线程执行的,当内存超过Redis内存上限后,每次写入都需要淘汰一些key,也会 造成耗时变长。
  • AOF刷盘开启always机制:每次写入都需要把这个操作刷到磁盘,写磁盘的速度远比写内存慢,会拖慢Redis的性能
  • 主从全量同步生成RDB:虽然采用fork子进程生成数据快照,但fork这一瞬间也是会阻塞整个线程的,实例越大,阻塞时间越久

解决办法:

  • 需要业务人员去规避
  • Redis在4.0推出了lazy-free机制,把bigkey释放内存的耗时操作放在了异步线程中执行,降低对主线程的影响

2、并发量非常大时,单线程读写客户端IO数据存在性能瓶颈,虽然采用IO多路复用机制,但是读写客户端数据依旧是同步IO,只能单线程依次读取客户端的数据,无法利用到CPU多核。

解决办法:

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

参考资料

Redis基础篇(二)高性能IO模型的更多相关文章

  1. 03 高性能IO模型:采用多路复用机制的“单线程”Redis

    本篇重点 三个问题: "Redis真的只有单线程吗?""为什么用单线程?""单线程为什么这么快?" "Redis真的只有单线程吗? ...

  2. 高性能IO模型浅析

    高性能IO模型浅析 服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: (1)同步阻塞IO(Blocking IO):即传统的IO模型. (2)同步非阻塞IO(Non-blocking  ...

  3. 高性能IO模型浅析(彩图解释)good

    服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: (1)同步阻塞IO(Blocking IO):即传统的IO模型. (2)同步非阻塞IO(Non-blocking IO):默认创建的s ...

  4. 高性能IO模型浅析(转)

    转自:http://www.cnblogs.com/fanzhidongyzby/p/4098546.html 是我目前看到的解释IO模型最清晰的文章,当然啦,如果想要详细的进一步了解还是继续啃蓝宝书 ...

  5. 【珍藏】高性能IO模型浅析

    服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: (1)同步阻塞IO(Blocking IO):即传统的IO模型. (2)同步非阻塞IO(Non-blocking IO):默认创建的s ...

  6. [转载] 高性能IO模型浅析

    转载自http://www.cnblogs.com/fanzhidongyzby/p/4098546.html 服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: (1)同步阻塞IO(B ...

  7. 转 高性能IO模型浅析

    高性能IO模型浅析 转自:http://www.cnblogs.com/fanzhidongyzby/p/4098546.html 服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: ( ...

  8. 【转载】高性能IO模型浅析

    服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: (1)同步阻塞IO(Blocking IO):即传统的IO模型. (2)同步非阻塞IO(Non-blocking IO):默认创建的s ...

  9. php基础篇-二维数组排序 array_multisort

    原文:php基础篇-二维数组排序 array_multisort 对2维数组或者多维数组排序是常见的问题,在php中我们有个专门的多维数组排序函数,下面简单介绍下: array_multisort(a ...

随机推荐

  1. 蓝桥杯——复数幂 (2018JavaAB组第3题)

    18年Java蓝桥杯A组第3题和B组是一样的. 第三题往往比较难. 复数幂 (18JavaAB3) (A.B两卷第三题一样) 设i为虚数单位.对于任意正整数n,(2+3i)^n 的实部和虚部都是整数. ...

  2. jvm系列(二)jvm垃圾收集器与内存分配策略

    众所周知,在java语言中,内存分配和回收是由jvm自动管理的.因此内存的分配和回收也是jvm三大功能之一.垃圾收集器(GC)需要完成三件事情: 哪些内存需要回收? 什么时候进行回收? 如何回收? 本 ...

  3. python 二维数组赋值问题

    [[]]是一个含有一个空列表元素的列表,所以[[]]*3表示3个指向这个空列表元素的引用, 修改任何一个元素都会改变整个列表 所以需要用另外一种方式进行创建多维数组,以免浅拷贝 >>> ...

  4. Bootstrap Blazor 组件介绍 Table (三)列数据格式功能介绍

    Bootstrap Blazor 是一套企业级 UI 组件库,适配移动端支持各种主流浏览器,已经在多个交付项目中使用.通过本套组件可以大大缩短开发周期,节约开发成本.目前已经开发.封装了 70 多个组 ...

  5. MacOS JMeter安装(多图)

    本文基于 MacOS 环境下进行 Jmeter 的安装. 一.下载JMeter 本文选用 JMeter 5.3 版本安装,5.3 版本需要 JDK 1.8 + 版本环境. Jmeter 5.3 下载: ...

  6. 20190713_发布网站的时候报错:无法完成向远程代理 URL 发送请求 基础连接已经关闭 发送时发生错误

    环境介绍: Vs2017 IIS 7.5 服务器: windows 2008 R2 overflow上有一篇帖子讲了关于问题的解决办法: 链接: https://stackoverflow.com/q ...

  7. Zookeeper(5)---分布式锁

    基于临时序号节点来实现分布式锁 为什么要用临时节点呢?如果拿到锁的服务宕机了,会话失效ZK自己也会删除掉临时的序号节点,这样也不会阻塞其他服务. 流程: 1.在一个持久节点下面创建临时的序号节点作为锁 ...

  8. 小程序使用动画时的 px 单位 转 rpx的方法

    借助API wx.getSystemInfoSync(); 通过API可获取的值: // 在 iPhone6 下运行: var systemInfo = wx.getSystemInfoSync(); ...

  9. When you received Ubuntu...

    翻译软件 Goldendict 安装命令: sudo apt install goldendict 在 dit -> Dictinoaries -> Websites 中添加有道的链接: ...

  10. Centos7网卡绑定的方法

    温和的方式请参考:https://www.cnblogs.com/zzf0305/p/9594093.html 一:传统的bond方式(饭已验证)------------本种的绑定方式比较暴躁 (1) ...