redis为什么效率高?
◆纯内存操作
◆单线程操作,避免了频繁的上下文切换
◆采用了非阻塞 I/O 多路复用机制
什么是I/O多路复用?
首先明确一个问题,常用的IO操作谈起,比如read和write,通常IO操作都是阻塞I/O的,也就是说当你调用read时,如果没有数据收到,那么线程或者进程就会被挂起,直到收到数据。这样,当服务器需要处理1000个连接的的时候,而且只有很少连接忙碌的,那么会需要1000个线程或进程来处理1000个连接,而1000个线程大部分是被阻塞起来的。由于CPU的核数或超线程数一般都不大,比如4,8,16,32,64,128,比如4个核要跑1000个线程,那么每个线程的时间槽非常短,而线程切换非常频繁。这样是有问题的:
线程是有内存开销的,1个线程可能需要512K(或2M)存放栈,那么1000个线程就要512M(或2G)内存。
线程的切换,或者说上下文切换是有CPU开销的,当大量时间花在上下文切换的时候,分配给真正的操作的CPU就要少很多。
那么,我们就要引入非阻塞I/O的概念,非阻塞IO很简单,通过fcntl(POSIX)或ioctl(Unix)设为非阻塞模式,这时,当你调用read时,如果有数据收到,就返回数据,如果没有数据收到,就立刻返回一个错误,如EWOULDBLOCK。这样是不会阻塞线程了,但是你还是要不断的轮询来读取或写入。
(图片来源:http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch06lev1sec2.htmll)
于是,我们需要引入IO多路复用的概念。多路复用是指使用一个线程来检查多个文件描述符(Socket)的就绪状态,比如调用select和poll函数,传入多个文件描述符,如果有一个文件描述符就绪,则返回,否则阻塞直到超时。得到就绪状态后进行真正的操作可以在同一个线程里执行,也可以启动线程执行(比如使用线程池)。
(图片来源:http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch06lev1sec2.htmll)
这样在处理1000个连接时,只需要1个线程监控就绪状态,对就绪的每个连接开一个线程处理就可以了,这样需要的线程数大大减少,减少了内存开销和上下文切换的CPU开销。
就是我们的 redis-client 在操作的时候,会产生具有不同事件类型的 Socket。
在服务端,有一段 I/O 多路复用程序,将其置入队列之中。然后,文件事件分派器,依次去队列中取,转发到不同的事件处理器中。
作者:用心阁
链接:https://www.zhihu.com/question/28594409/answer/74003996
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。