单线程Redis 为什么那么快

发布时间:2022-06-27 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了单线程Redis 为什么那么快脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

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

其实,这是Redis多方面设计选择的一个综合结果。一方面,Redis的大部分操作在内存上完成,再加上它采用了高效的数据结构,例如哈希表和跳表,这是它实现高性能的一个重要原因。另一方面,就是Redis采用了多路复用机制,使其在网络IO操作中,能并发处理大量的客户端请求,实现高吞吐率

接下来,重点说下多路复用机制。

首先,我们要弄明白网络操作的基本IO模型和潜在的阻塞点。毕竟,Redis采用单线程进行IO,如果线程被阻塞了,就无法进行多路复用了。

单线程Redis 为什么那么快

以Get请求为例,为了处理一个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模型中,不同操作调用后,会返回不同的套接字类型。

单线程Redis 为什么那么快

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模型

单线程Redis 为什么那么快

多个FD就是刚才所说的监听套接字。Redis网络框架调用epoll机制,让内核监听这些套接字。此时,Redis线程不会阻塞在某一个特定的监听,或已连接套接字上,也就是说,不会阻塞在某一个特定的客户端请求处理上。正因为此,Redis可以同时和多个客户端连接并处理请求,从而提升并发性。为了在请求到达时能通知到Redis线程,select/epoll提供了基于事件的回调机制,即针对不同事件的发生,调用相应的处理函数

那么,回调机制是怎么工作的呢?

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

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

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

这就像病人去医院瞧病。在医生实际诊断前,每个病人(等同于请求)都需要先分诊、测体温、登记等。如果这些工作都由医生来完成,医生的工作效率就会很低。所以,医院都设置了分诊台,分诊台会一直处理这些诊断前的工作(类似于Linux内核监听请求),然后再转交给医生做实际诊断。这样即使一个医生(相当于Redis单线程),效率也能提升。

不过,需要注意的是,即使你的应用场景中,部署了不同的操作系统,多路复用机制也是适用的。因为这个机制的实现有很多种,既有基于Linux系统下的select和epoll实现,也有基于FreeBSD 的 kqueue 实现,以及基于Solaris的evport实现

这样,你可以根据Redis实际运行的操作系统,选择相应的多路复用实现。

通过刚才的学习,现在应该了解了Redis单线程了吧,是指它对网络IO和数据读写的操作,采用了一个线程,而采用单线程的一个核心原因,是避免多线程开发的并发控制问题。单线程的Redis也能获得高性能,跟多路复用的IO模型密切相关,因为这避免了accept()send()/recv()潜在的网络IO操作阻塞点。

了解更多可扫码关注公众号

单线程Redis 为什么那么快

脚本宝典总结

以上是脚本宝典为你收集整理的单线程Redis 为什么那么快全部内容,希望文章能够帮你解决单线程Redis 为什么那么快所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。
标签:并发