Redis中线程IO模型是什么

59次阅读
没有评论

共计 2176 个字符,预计需要花费 6 分钟才能阅读完成。

这篇文章将为大家详细讲解有关 Redis 中线程 IO 模型是什么,丸趣 TV 小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

Redis 是一个单线程的应用程序,NodeJs、Nginx 都是单线程,它们都属于服务器高性能的典范。

Redis 之所以是单线程还能这么快的原因:

其一是因为它所有的数据都在内存当中,所有的运算都是内存级别的运算,所以使用 redis 时,要注意时间复杂度为 O(n) 的指令,因为是单线程的,如果数据量太大,会让其他指令被阻塞等待;

其二是因为 redis 使用非阻塞 IO 与多路复用处理大量的客户端连接。

非阻塞 IO

当我们使用套接字的读写方法时,默认是阻塞的,

即调用 read 方法传递一个参数 n,表示最多读取 n 个字节后返回,如果一个字节都没有,线程就会在 read 方法这里持续等待,直到有数据过来或者连接被关闭,read 方法此时返回,线程才能执行下面的逻辑,

write 方法一般不会阻塞,除非内核为套接字分配的写缓冲区满了,write 方法才会阻塞,一直到缓存区中有空间闲出来。

下图是套接字读写的细节流程。

非阻塞 IO 在使用套接字时提供了一个选项 Non_Blocking,当这个选项打开时,读写方法不会阻塞,而是能读多少读多少,能写多少写多少,

能读多少取决与内核为套接字分配的读缓冲区的数据字节数,能写多少取决于内核为套接字写缓冲区分配的数据字节数,

读写方法都会通过返回值告诉程序读写了多少字节数。

非阻塞 IO 意味着读写时,线程不必再被阻塞着,读写可以瞬间完成,线程可以继续往下做别的事情。

多路复用 (事件轮询)

非阻塞 IO 虽然很快,但是也带来一个问题,线程读数据,读了一部分就返回了,没有读完,剩下的数据何时继续读?,写数据,缓冲区满了,没有写完,剩下的数据何时继续写?

当可以继续读或者可以继续写时,应该给应用程序一个通知,告诉应用程序可以继续读或者继续写,事件轮询 API 就是用来处理这个问题的。

select

操作系统提供了一个 select 函数给用户程序,输入是读写描述符列表 read_fds write_fds,输出是与之对应的可读可写事件,

同时还提供了 timeout 参数,线程最多等待 timeout 的时间,在这期间有事件过来,方法立刻返回,线程往下处理,如果超过 timeout 时间,方法也会返回,

如果拿到事件了,线程即可挨个处理相应的事件,处理完了以后继续调用 select api 轮询,所以该线程其实是一个死循环,不停的 select,不停的处理,来回这样,这个死循环被称之为事件循环,一个循环即一个周期。

事件循环伪代码:

while True
 read_events, write_events = select(read_fds, write_fds, timeout)
 for event in read_events:
 handle_read(event.fd)
 for event in write_events:
 handle_write(event.fd)
 handle_others() #  做其他的逻辑处理,处理定时任务等等 

通过 select 函数我们可以处理多个通道描述符的读写事件,所以将 select 这类的系统函数调用称之为多路复用 API,

现代操作系统的多路复用 API 已经不使用 select 系统调用,改用 epoll(linux) 和 kqueue(FreeBSD、macosx),

select 的性能在描述符变多时会变得很差,epoll 与 select 使用起来略有差异,不过都可以用上面的伪代码理解,都是当描述符发生事件时,循环对描述符的事件做出处理,

serversocket 对象的读操作是指调用 accept 接受客户端新连接,何时有连接来临,也是通过 select 调用的读事件通知的。

Java 中的 NIO 技术就是事件轮询,其他语言也有这个技术。

指令队列

Redis 为每一个客户端套接字关联一个指令队列,客户端发来的指令通过队列进行先进先出的顺序处理。

响应队列

同样 Redis 返回的结果也通过为每个客户端关联的一个队列返回,如果队列为空,则暂时不需要去获取写事件,

此时会将该客户端描述符从 write_fds 里移除,等队列有数据的时候,再将描述符放进去,这样可以避免 select 系统调用返回写事件时,发现没数据可写,造成空轮询、无用轮询,对机器 CPU 的消耗。

定时任务

服务器不单要响应 IO 事件,有些其他的事情也需要处理,例如应用程序自身的定时任务,如果线程阻塞在 select 调用上,等待 select 的返回,这会造成有些定时任务到期了,却没有执行,

Redis 的定时任务记录在一个称为 最小堆 的数据结构中,这个堆中,最快要执行的任务排在最上方,每个循环周期里,redis 会对堆中已经到时间点的任务进行处理,

处理完毕后,将堆中即将要执行的任务还需要的时间记录下来,再次调用 select 时,这个时间就是 timeout 的值,在这期间内不会有其他任务需要执行了,redis 可以放心的最多阻塞这么久,然后到时间后进行相应的处理。

NodeJs 和 Nginx 的事件处理原理和 Redis 也是类似的形式。

关于“Redis 中线程 IO 模型是什么”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-07-17发表,共计2176字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)