zoukankan      html  css  js  c++  java
  • 1. Redis是属于多线程还是单线程?不同版本之间有什么区别?

    Redis是普及率最高的技术之一,不仅是面试会被经常问到,在工作中也是非常常用的。下面我们就深入的了解一下Redis,顺便会介绍一些常见的面试题。

    问题:Redis是属于单线程还是多线程?

    不同版本的Redis是不同的,在Redis4.0之前,Redis是单线程运行的,但是单线程并不代表效率就低,像Nginx、Nodejs也是单线程程序,但是它们的效率并不低。

    原因就是Redis是基于内存操作的,它的瓶颈在于机器的内存、网络带宽,而不是CPU,在你CPU还没达到瓶颈时你的内存可能就先满了、或者带宽达到瓶颈了。因此CPU不是主要原因,那么自然就采用单线程了,况且使用多线程比较麻烦。但是在Redis4.0的时候,已经开始支持多线程了,比如后台删除等功能。

    简单来说,Redis在4.0之前使用单线程的模式是因为以下三个原因:

    • 使用单线程模式的Redis,其开发和维护会更简单,因为单线程模型方便开发和调试。
    • 即使使用单线程模型也能够并发地处理多客户端的请求,主要是因为Redis内部使用了基于epoll的多路复用(后面会说)。
    • 对于Redis来说,主要的性能瓶颈是内存或者网络带宽,而并非CPU。

    但Redis在4.0以及之后的版本中引入了惰性删除(也叫异步删除),意思就是我们可以使用异步的方式对Redis中的数据进行删除操作了,例如:unlink keyflushdb asyncflushall async,举个例子:

    127.0.0.1:6379> set name hanser
    OK
    127.0.0.1:6379> get name
    "hanser"
    127.0.0.1:6379> unlink name  # 这里是异步删除一个key,同步的话则是del name
    (integer) 1
    127.0.0.1:6379> flushdb async
    OK
    127.0.0.1:6379> flushall async
    OK
    

    这样处理的好处就是不会使Redis的主线程卡顿,会把这些删除操作交给后台线程来执行。

    通常情况下使用 del 指令可以很快的删除数据,但是当被删除的 key 是一个非常大的对象时,例如:删除的是包含了成千上万个元素的 hash 集合时,那么 del 指令就会造成 Redis 主线程卡顿,因此使用惰性删除可以有效的避免 Redis 卡顿的问题。

    考点分析:

    关于Redis线程模型的问题(单线程或多线程)几乎是Redis必问的问题之一,但是回答好的人却并不多,大部分的人只能回答上来Redis是单线程的以及说出单线程的众多好处,但对于Redis4.0和Redis6.0中,尤其是Redis6.0中多线程的特点,能够准确回答出来的人则非常少。关于单线程和多线程的相关知识,还有以下面试题。

    • Redis主线程既然是单线程的,为什么还这么快?
    • 介绍一下Redis中的IO多路复用?
    • 介绍一下Redis6.0中的多线程?

    1. Redis为什么这么快?

    我们知道Redis4.0之前是单线程的,那既然是单线程为什么速度还能那么快?

    原因有以下几点:

    • 基于内存操作:Redis的所有数据都在内存中,因此所有的运算都是内存级别的,所以它的性能比较高。
    • 数据结构简单:Redis的数据结构比较简单,是为Redis专门设计的,而这些简单的数据结构的查找和操作的时间复杂度都是O(1)
    • 多路复用和非阻塞I/O:Redis使用I/O多路复用功能来监听多个socket连接客户端,这样就可以使用一个线程来处理多个情况,从而减少线程切换带来的开销,同时也避免了I/O阻塞操作,从而大大地提高了Redis的性能。
    • 避免上下文切换:因为是单线程模型,因此就避免了不必要的上下文切换和多线程竞争,这就省去了多线程切换带来的时间和性能上的开销,而且单线程不会导致死锁的问题发生。

    官方使用的基准测试结果表明,单线程的Redis可以达到10W/S的吞吐量。

    2. I/O多路复用是什么?

    套接字的读写方法默认是阻塞的,例如当调用读取操作read方法时,缓冲区没有任何数据,那么这个线程会卡在这里,直到缓冲区中有数据或者连接被关闭时,read方法才会返回,该线程才能继续处理其它业务。

    但这样显然就降低了程序的整体执行效率,而Redis使用的是非阻塞的I/O,这就意味这I/O的读写流程不再是阻塞的,读写方法都是瞬间完成并且返回的,也就是它会采用能读多少就读多少、能写多少就写多少的策略来执行I/O操作,这显然更符合我们对性能的追求。

    但是这种非阻塞的I/O也面临一个问题,那就是当我们执行读取操作时,有可能只读取了一部分数据;同理写数据也是这种情况,当缓冲区满了,而我们的数据还没有写完,那么剩下的数据何时可写就成了一个问题。

    而I/O的多路复用就是解决上面的这个问题的,使用I/O多路复用最简单的方式就是使用select函数,此函数是操作系统提供给用户程序的API接口,用于监控多个文件描述符的可读和可写情况的,这样就可以监控到文件描述符的读写事件了。当监控到相应的事件之后就可以通知线程处理相应的业务了,这样就保证了Redis读写功能的正常执行。

    不过现在的操作系统已经基本上不使用select函数了,改为调用epoll函数(Linux)了,macOS则是使用Kqueue(继承于Unix),因为select函数在文件描述符非常多的时候性能非常的差。

    3. Redis6.0中的多线程?

    Redis单线程的优点很明显,不但降低了Redis内部实现的复杂性,也让所有操作都可以在无锁的情况下进行,并且不存在死锁和线程切换带来的性能以及时间上的消耗;但是其缺点也很明显,单线程机制导致Redis的QPS(Query Per Second,每秒查询数)很难得到有效的提高(虽然已经够快了,但人毕竟还是要有更高的追求的)

    Redis在4.0版本中虽然引入了多线程,但是此版本的多线程只能用于大数据量的异步删除,对于非删除操作的意义并不是很大。

    如果我们使用Redis多线程就可以分摊Redis同步读写I/O的压力,以及充分利用多核CPU资源,并且可以有效地提升Redis的QPS。在Redis中虽然使用了I/O多路复用,并且是基于非阻塞I/O进行操作的,但是I/O的读和写本身是阻塞的。比如当socket中有数据时,Redis会先将数据从内核态空间拷贝到用户态空间,然后再进行相关操作,而这个拷贝过程是阻塞的,并且当数据量越大时拷贝所需要的时间就越多,而这些操作都是基于单线程完成的。

    因此在Redis6.0中新增了多线程的功能来提高I/O的读写性能,它的主要实现思路是将主线程的I/O读写任务拆分给一组独立的线程去执行,这样就可以使多个socket的读写并行化了,但Redis的命令依旧是由主线程串行执行的。

    但是注意:Redis6.0是默认禁用多线程的,但可以通过配置文件redis.conf中的io-threads-do-reads等于true来开启,完整配置为io-threads-do-reads true。但是还不够,除此之外我们还需要设置线程的数量才能正确地开启多线程的功能,同样是修改Redis的配置,例如设置io-threads 4,表示开启4个线程。

    关于线程数的设置,官方的建议是如果为4核的CPU,那么设置线程数为2或3;如果为8核的CPU,那么设置线程数为6。总之线程数一定要小于机器的CPU核数,线程数并不是越大越好。

    关于Redis的性能,Redis的作者在2019的RedisConf大会上提到,Redis6.0引入的多线程I/O特性对性能的提升至少是一倍以上。国内也有人在阿里云使用4个线程的Redis版本和单线程的Redis进行比较测试,发现测试的结果和Redis作者说的一致,性能基本可以提高一倍。

    总结

    这一节我们介绍了Redis在4.0之前单线程依然快的原因:基于内存操作、数据结构简单、I/O多路复用和非阻塞I/O、避免了不必要的线程上下文切换。并且在Redis4.0开始支持多线程,主要体现在大数据的异步删除上面,例如:unlink key、flushdb async、flushall async等。而Redis6.0的多线程则增加了对I/O读写的并发能力,用于更好的提升Redis的性能。

  • 相关阅读:
    SQL联结(Join)的命令详解
    Symbian c++在程序安装时显示一份免责声明
    Effective C++条款11: 为需要动态分配内存的类声明一个拷贝构造函数和一个赋值操作符
    <转>S60系统出错问题汇总
    开发规范C#程序
    Javascript 进行decode编码,C#中进行解码的问题
    IIS7.5 不能访问3.5 wcf 的解决办法
    开发规范总结数据库
    [转载]Linux性能测试 tcpdump命令
    [转载]Linux性能测试 top命令
  • 原文地址:https://www.cnblogs.com/traditional/p/13273089.html
Copyright © 2011-2022 走看看