当前位置: 首页 > 科技观察

Redis不是一直宣称单线程效率也很高吗,为什么又要用多线程呢?

时间:2023-03-12 23:00:32 科技观察

Redis是目前比较知名的内存数据库,在各种场景下有着非常丰富的应用。前段时间Redis推出了6.0版本,新版本采用了多线程模型。因为我们公司使用的内存数据库是自研的,按理说我对Redis不是很关注,但是因为Redis的应用比较广泛,所以需要了解一下,这样才能进行面试。应聘者不可能用过Redis,但是不得不问一下阿里的Tair是怎么回事。所以,在Redis6.0上线之后,想明白为什么要用多线程。现在使用的多线程和以前的版本有什么区别?为什么多线程用的这么晚?Redis不是已经采用了多路复用技术吗?不是号称性能很高吗?为什么要使用多线程模型?本文将分析这些问题及其背后的思考。为什么Redis最初设计为单线程?Redis作为一个成熟的分布式缓存框架,由网络请求模块、索引模块、存储模块、高可用集群支持模块、数据操作模块等很多模块组成。很多人说Redis是单线程的,其实他们认为Redis中所有模块的操作都是单线程的。其实,这是错误的。我们所说的Redis单线程是指“它的网络IO和键值对读写都是一个线程完成”,也就是说Redis中只有网络请求模块和数据操作模块是单线程的。其他的,例如持久存储模块和集群支持模块,是多线程的。所以,Redis并不是没有多线程模型。早在Redis4.0时,部分命令已经实现了多线程。那么,为什么网络操作模块和数据存储模块一开始没有采用多线程呢?这个问题的答案比较简单!因为:“不需要!”为什么没有必要?先说说什么时候用多线程?多线程应用场景计算机程序在执行过程中,主要需要进行两个操作:读写操作和计算操作。其中,读写操作主要涉及I/O操作,包括网络I/O和磁盘I/O。计算操作主要涉及CPU。多线程的目的是以并发方式提高I/O利用率和CPU利用率。那么,Redis是否需要通过多线程来提高I/O利用率和CPU利用率呢?首先我们可以肯定的说Redis不需要增加CPU利用率,因为Redis的操作基本都是基于内存的,CPU资源根本不是Redis的性能瓶颈。因此,完全没有必要通过多线程技术来提高Redis的CPU利用率。那么,如何利用多线程技术来提高Redis的I/O利用率呢?有必要吗?Redis确实是一个I/O密集型框架。在其数据操作过程中,会发生大量的网络I/O和磁盘I/O。要想提高Redis的性能,就必须提高Redis的I/O利用率,这是毋庸置疑的。然而,多线程技术并不是提高I/O利用率的唯一途径!多线程的缺点我们在很多文章中介绍了Java中的一些多线程技术,比如内存模型、锁、CAS等,这些都是Java中提供的一些技术,用来保证多线程情况下的线程安全。线程安全:是编程中的一个术语,意思是在并发环境下调用一个函数或函数库时,能够正确处理多个线程之间的共享变量,从而使程序功能能够正确完成。与Java类似,所有支持多线程的编程语言或框架都不得不面对一个问题,即如何解决多线程编程模型带来的共享资源的并发控制问题。虽然多线程可以帮助我们提高CPU和I/O的利用率,但是多线程带来的并发问题也给这些语言和框架带来了更多的复杂性。而且,在多线程模型中,多线程之间的切换也会带来一定的性能开销。因此,在提高I/O利用率方面,Redis并没有采用多线程技术,而是选择了多路复用I/O技术。总结Redis在网络请求模块和数据操作模块没有采用多线程模型,主要有以下四点原因:1.Redis操作是基于内存的,大部分操作的性能瓶颈不在CPU。2.使用单线程模型,可以更高的可维护性,更低的开发、调试和维护成本3.单线程模型,避免了线程间切换带来的性能开销4.在单线程中使用多路复用I/O技术可以同样提高RedisI/O利用率的性能还是要记住:Redis并不是完全单线程的,关键的网络IO和键值对的读写都是一个线程完成的。Redis多路复用多路复用这个词相信很多人都不陌生。我在之前的很多文章中都提到过这个词。其中,我们在介绍LinuxIO模型的时候提到过,在介绍HTTP/2原理的时候也提到过。那么,Redis的多路复用技术和我们之前介绍的有什么区别呢?先说Linux的多路复用技术,也就是可以把多个进程的IO注册到同一个管道上,这个管道会统一和内核交互。当管道中某个请求所需的数据准备好后,进程将相应的数据复制到用户空间。再看看上面的图片和上面的那句话,以后可能会用到。也就是说多个IO流由一个线程处理。IO多路复用在Linux下包括三种,select、poll、epoll。从抽象的角度来看,它们的功能是相似的,但具体的细节是不同的。其实Redis的IO多路复用程序的所有功能都是通过包装操作系统的IO多路复用函数库来实现的。每个IO多路复用函数库在Redis源码中都有对应的独立文件。在Redis中,每当一个socket准备好进行连接应答、写入、读取、关闭等操作时,就会产生一个文件事件。由于一个服务器通常会连接多个套接字,因此可能会同时发生多个文件事件。一旦有请求到达,就会交给Redis线程处理,实现了一个Redis线程处理多个IO流的效果。因此Redis选择使用多路复用IO技术来提高I/O利用率。Redis之所以能有这么高的性能,除了使用了多路复用技术和单线程外,还有以下几个原因:1.它完全基于内存,大部分请求都是纯内存操作,非常快.2、数据结构简单,数据操作也简单,比如hashtable和skiptable,性能高。3、使用单线程避免了不必要的上下文切换和竞争条件,不会因为多进程或多线程引起的切换而消耗CPU4、使用多通道I/O多路复用模型Redis6.0为什么要引入多线程2020年5月,Redis正式推出6.0版本。该版本有许多重要的新特性,其中多线程特性引起了广泛关注。不过需要提醒大家的是,Redis6.0中的多线程只是在处理网络请求时使用了多线程,而数据的读写命令仍然是单线程处理的。不过,不知道有没有人会有这样的疑问:Redis不是号称单线程、高性能吗?不是说多路复用技术大大提高了IO利用率吗?为什么我们需要多线程?主要是我们对Redis的要求比较高。据计算,Redis将所有数据放在内存中,内存的响应时间约为100纳秒。对于小数据包,Redis服务器可以处理80,000到100,000QPS,这样高的数据对于80%的公司来说,单线程Redis使用绰绰有余。但是,随着业务场景越来越复杂,一些公司动辄上亿的交易量,需要更大的QPS。为了提高QPS,很多公司都部署了Redis集群,尽可能的增加Redis机器的数量。但是这种方式的资源消耗是巨大的。经过分析,限制Redis性能的主要瓶颈出现在网络IO的处理上,虽然之前使用了多路复用技术。但是正如我们前面提到的,多路复用IO模型本质上仍然是一种同步阻塞IO模型。下面是多路复用IO中select函数的处理过程:从上图可以看出,在多路复用IO模型中,在处理网络请求时,调用select(其他函数同理)的过程是阻塞的,也就是说,这个进程会阻塞线程。如果并发度很高,这可能会成为瓶颈。虽然现在很多服务器都有多个CPU核心,但是对于Redis来说,因为使用了单线程,在一个数据操作过程中,大量的CPU时间片花在了网络IO的同步处理上。并没有充分发挥多核的优势。如果可以使用多线程,让网络并发处理请求,性能可以有很大的提升。多线程除了可以减少网络I/O等待带来的影响外,还可以充分利用CPU的多核优势。因此,Redis6.0使用了多个IO线程来处理网络请求。网络请求的解析可以交由其他线程完成,然后将解析后的请求交给主线程进行实际的内存读写。提高网络请求处理的并行性,从而提高整体性能。但是Redis的多IO线程只是用来处理网络请求的。对于读写命令,Redis仍然使用单线程来处理。那么,引入多线程之后,如何解决并发带来的线程安全问题呢?这就是为什么我们之前多次提到“Redis6.0的多线程只是用来处理网络请求,数据的读写还是单线程”的原因。Redis6.0只是在接收和解析网络请求时,以及请求的数据通过网络返回给服务器时才使用多线程。数据的读写操作仍然是单线程完成的,这样不会有并发问题。参考资料:https://www.cnblogs.com/Zzbj/p/13531622.htmlhttps://xie.infoq.cn/article/b3816e9fe3ac77684b4f29348https://jishuin.proginn.com/p/763bfbd2a1c2