Redis 为啥这么快?

Redis 3.x单线程时代但性能依旧很快的主要原因

 

 

  1. 基于内存操作:所有数据都存于内存中,读写速度非常快,内存的响应时长约为100纳秒,运算都是内存级别的,因此性能比较高;

  2. 数据结构简单:常用的数据结构中有些是专门设计的,如采用自己设计的简单动态字符串(Simple Dynamic String)作为字符串对象的底层数据结构,将获取字符串长度的时间复杂度提高到O(1)等特点;

  3. I/O多路复用:使用 I/O多路复用功能来监听多个 socket 连接客户端,也就能够使用一个线程来处理多个请求,达到并发处理请求的效果;

  4. 避免上下文切换:单线程模型,避免了多线程切换和多线程竞争带来的时间和性能上的消耗,并且避免了死锁问题的发生。

 

 

Redis 4.0 之前一直采用单线程的主要原因有以下三个:

 

 

  1. 使用单线程模型,开发和维护简单;

  2. 通过使用非阻塞的I/O多路复用模型,能够并发的处理多客户端的请求;

  3. Redis 主要的性能瓶颈是 memory 或者 network bound 而并非 CPU。(最主要,也是决定性原因)

 

 

Redis 4.0 针对删除操作引入多线程的原因:

 

 

正常情况下,也就是针对小数据两量对象的删除是没有问题的,但是当删除的对象非常大,如几十兆的对象,短时间无法释放内存空间,此时,单线程删除就会阻塞到其它读写操作的进行。

 

 

因此,很自然的想法是不让主线程再处理删除操作了,开启一个异步线程去慢慢的删除对象就好了。Redis 4.0 引入了unlink key、flushall async、FLUSHDB ASYNC 等命令, 对删除操作进行懒处理,主要用于 Redis 数据的异步删除。其处理读写请求的仍然只有一个线程,所以仍然称Redis为单线程的。(Redis之父一开始还是想用单线程去处优化,但是在系统繁忙时性能下降幅度较大,因此才使用了异步线程的方法)

 

 

Redis 6.0 引入多线程提高网络I/O性能的原因:

 

 

随着互联网业务系统的流量越来越大,Redis的网络I/O性能瓶颈越来越明显;单线程不能充分利用现代计算机多核CPU的优势,虽然官方建议多开几个实例就可以利用多核。

 

 

因此,在 Redis 6.0 中引入了 I/O 多线程的读写,主要思想是主线程不再进行 socket 的数据读写,而是开启一组独立的线程去监控处理 socket 的数据读写和请求解析等,达到并行化读写效果,这样就可以更加高效的并发的处理网络I/O。其中读写过程是多线程的,命令的执行实际上还是单线程处理。

 

 

Redis6.0中,多线程机制默认是关闭的,如果需要使用多线程功能,需要在redis.conf中进行配置:1 io-thread-do-reads yes,表示启动多线程。2 io-thread 3设置线程个数。官方的建议是 4 核的 CPU建议线程数设置为 2 或 3,8 核 CPU 建议设置为 6,线程数一定要小于机器核数,线程数并不是越大越好。

 

 

I/O 多路复用模型的发展

 

 

Unix网络编程中共包含五种IO模型,分别是Blocking IO(阻塞IO)、NoneBlocking IO(非阻塞IO)、IO multiplexing(IO多路复用)、signal driven IO(信号驱动IO)、asynchronous IO(异步IO)。Redis使用IO多路复用模型实现,本文重点讲解阻塞IO发展为非阻塞IO,继而发展出IO多路复用模型的过程,至于信号驱动IO和异步IO,本文不作介绍。

 

 

基本概念

 

 

用户空间和内核空间

 

 

首先说一下CPU指令集的概念,CPU指令集是实现软件执行硬件的指令的集合。CPU指令操作的不规范,会对整个系统产生重大影响。因此CPU指令集必须设置权限,如Intel厂商将CPU指令集的权限由高到低分为ring 0 、ring 1、ring 2、ring 3分为。Linux系统中通常只使用ring 0 、ring 3两种权限:ring 0 权限最高,可以使用所有CPU指令;ring 3 权限最低,仅能使用常规CPU指令,不能直接访问硬件和所有内存。(下文介绍的用户态和内核态可以简单理解为分别对应了ring 3 、ring 0权限的指令集。)好了,这里为什么是不能访问所有内存呢?

 

 

在内存资源上,若任由应用程序随意访问内存空间的任意位置,很可能由于应用程序的运行或错误造成系统崩溃,如删除所有内存数据操作。故应用程序访问任意内存空间是很危险的。因此,操作系统将内存划分为操作系统内核使用的内存空间和应用程序访问的内存空间,比如说Linux系统中内存为4G,其中高地址的1G内存是专由操作系统内核使用的,该部分内存就称为内核空间;低地址的3G内存由应用程序使用,被称为用户空间。但并不是说系统内核不能使用低地址的3G内存空间,即内核空间只能由系统内核使用,用户空间能被系统内核和应用程序使用。

 

 

用户态和内核态

 

 

顾名思义,运行在用户空间时的进程或线程处于用户态,运行在内核空间时的进程或线程处于内核态。 用户态切换到内核态包含3种方式: 系统调用、软中断(如异常)、硬中断(如I/O操作)。

 

 

为什么说用户态和内核态的切换开销大?

 

 

例如,由用户态切换为内核态时,需要记录用户态的状态如寄存器等上下文、复制用户态参数到内核态、一致性和安全性检查等。如是再切换为用户态,则需要复制数据到用户态,并且回复用户态上下文。这个过程是比较耗时的,因此说两态之间的切换开销大。

posted @ 2022-04-26 11:21  piaobodeyun0000  阅读(67)  评论(0编辑  收藏  举报