一文带你完全掌握Redis为何这么快?

1、Redis为何这么快

由于Redis采起了epoll模型,IO多路复用。同步非阻塞。java

2、什么是epoll

介绍epoll以前必需要先介绍下select和poll。明白select和poll的缺点后才能知道为何epoll这么快。web

3、什么是select

一、源码

在这里插入图片描述

二、源码解析

2.一、核心代码

// select(xx,读文件描述符集合,写文件描述符集合,异常文件描述符集合,超时时间),咱们只须要关注读文件描述符集合,其余NULL不影响,由于有默认值。
select(max+1, &rset, NULL, NULL, NULL)

2.二、&rset是什么?

rset其实就是fds(文件描述符的集合)。而select模型存储fds的方式是采起的bitmap,默认最大1024个。面试

2.三、执行流程

1.select模型每次都直接将rset(也就是fds)所有拷贝到内核态,由于内核态速度比用户空间态快不少。
2.若是没数据的话,select函数会阻塞,若是有数据的话会执行两步
(1)将有数据的那个fd置位(也就是标记一下,表明这个fd有数据)
(2)select函数不在阻塞,将继续往下执行。也就是总体遍历fds,找到有数据的那个fd读取数据作处理。他的fd不能重用,每一次都须要从新建立新的fds且将用户空间态的fds拷贝到内核态。(这个能够看上面while(1)的代码)
在这里插入图片描述redis

三、缺点

  • fds最大支持1024个(能够更改,可是意义不大)
  • fd不可重用,每次内核态都给置位了,致使每次都须要从新执行以下四句话建立一个新的rset(也就是fds)
    在这里插入图片描述
  • 用户控件态拷贝rset到内核态也须要时间,虽然内核态执行比用户态快,可是copy也须要开销
  • O(n)再次遍历问题。由于rset里的fd被置位后,select函数并不知道哪一个被置位了,须要从头遍历到尾,逐个对比。

4、什么是poll

1.源码

在这里插入图片描述

二、源码解析

2.一、核心代码

// 表明传入了5个pollfd这个结构体,超时时间是5000,poll总体就是围绕他的pollfd这个结构体来展开的
poll(pollfds, 5, 5000)

2.二、结构体

poll的结构体是为了fd重复利用,不须要每次都拷贝到内核态用的。
在这里插入图片描述
revents:置位用的,若是某个fd有数据了,就把这个revents置位为POLLIN编程

2.三、执行流程

  • 有数据的时候流程以下
  • 将pollfd这个结构体里的revents这个字段置位为POLLIN
  • 而后判断这个结构体的revents字段是否是被置位为POLLIN了,是的话再将其结构体的这个字段恢复默认值0,而后取出数据,处理逻辑。
  • 恢复为默认值0的用途是不须要在像select同样每次都须要从新建立fds,而是直接复用。但仍是避免不了每一个fd第一次复制一次。好的是不用每次都建立新的了。
    在这里插入图片描述

三、解决了select哪些问题

  • 采起的链表存储,而不是bitmap,解决了1024长度限制问题
  • 采起结构体每次置位结构体内的revents字段,而不破坏fd自己,因此可重用,不须要每次都建立新的fd。

四、缺点

  • 用户控件态拷贝rset到内核态也须要时间,虽然内核态执行比用户态快,可是copy也须要开销
  • O(n)再次遍历问题。由于rset里的fd被置位后,select函数并不知道哪一个被置位了,须要从头遍历到尾,逐个对比。

5、再谈epoll

0、概述

epoll的出现就是为了解决poll最后两个缺点的。数组

一、源码

在这里插入图片描述

二、源码解析

2.一、核心代码

epoll_create 至关于建立白板
epoll_ctl至关于往白板上写字,写的内容是一个fd、一个events结构体,events结构体跟pollfd的结构体很像。
epoll_ctl执行完后就获得了epfd,epfd就是循环了五个fd-events放里面,每一个fd都有events事件
epoll_wait

2.二、执行流程

  • epoll将fd放到了红黑树里,且不须要拷贝到内核态,由于他采起了“共享内存”的概念。(其实仍是复制,只是复制采起了其余技术可使开销极其的小)
  • epoll的置位是重排,好比五个fd, 1 2 3 4 5,1 3 5这三个fd有数据了,那么他会重排序,排成以下1 3 5 2 4。(也有的说是单独放到新的数组里)
  • 每一次置位nfds的值都+1。且会回调epoll_wait
  • 因此epoll_wait执行完会返回有几个fd有数据,那么下面的for直接遍历nfds次便可。解决了前面的两种O(n)。变成了O1
    在这里插入图片描述

6、阻塞/非阻塞

用户进程经过调用select/poll/epoll去处理socket请求的时候, select/poll/epoll因为是阻塞的,因此致使用户进程阻塞;而select/poll/epoll处理的socket用的是非阻塞微信

7、再次分析Redis

好比三个redis-cli,假设2个redis-cli写入命令,
select:那么select模型是轮询这三个redis-cli的fd,看哪一个fd有消息,有的话读取处理消息。当他下次再写命令的时候还须要从新建立fd,而后复制到内核态而后再遍历所有。
poll:那么poll模型是轮询这三个redis-cli的fd,看哪一个fd有消息,有的话读取处理消息。下次再写入的时候仍是遍历全局fd,看哪一个fd有消息进行处理。省去了每次都建立新的fd且复制的过程。
epoll:epoll就不轮询了,有消息进来后你通知我,我去处理你的消息,那些没消息的fd我无论。并且复制到内核态的过程我采起牛逼的技术让开销达到最小的极致。socket

8、总结

  • select、poll、epoll必须懂,redis面试90%问
    -在这里插入图片描述

9、我的公众号

微信公众号【Java码农社区】
在这里插入图片描述svg