Redis 为什么这么快,你知道 I/O 多路复用吗?

webff · · 1830 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

今天我们讨论一下 redis 面试高频题,为什么 Redis 那么快? 首先,你可以先想一下答案,我先说下大家普遍的答案: - 单线程 - 基于内存操作,速度快 - I/O 多路复用 相信很多人第一时间回答出来上面这些,那么面试官一般会接着问,所有的操作都是单线程吗?单线程为什么快呢,什么是 I/O 多路复用?很多人这个时候就会 G 了。 今天我们好好聊聊,首先我们要知道,我们常说的 Redis 是单线程,主要是指 Redis 在网络 I/O 和 键值的读写操作是有一个线程来完成的,但是其他的一些功能是由其他的线程来执行的,如: - 持久化数据落盘 - 集群主从同步 - 异步数据删除 所以严格来说,Redis 并不是单线程的。但是在面试中,我们可以先铺垫一下,如果面试官刨根问底,显然是不够的。 ### 1. Redis 为什么用单线程 日常开发中,我们接触都更多是多线程,为了能够提升系统的请求数或者是吞吐率,我们会采用多线程来实现。看下图:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9ecca87ba80449c5a07d9fa186f59a1e~tplv-k3u1fbpfcp-zoom-1.image) 为啥会出现预期和实际结果不一样的情况?其实是当出现多线程同时在操作一个共享的资源时,我们为了保证结果的正确性,我们需要有额外的开销来保证,如锁。当有锁出现了,我们就需要在考虑在什么时候需要获取锁,释放锁,其中就需要记录锁的状态,可想而知,性能就会下降。这就是多线程模式在面临高并发场景下共享资源的访问问题。 所以大家应该知道为啥 Redis 要用单线程了吧,我们接着来说说为什么单线程的 Redis 为啥能获得高性能的原因。 ### 2. 单线程 Redis 为什么那么快 我们正常理解下,多线程在处理能力上是要高于单线程的,但是为啥 Redis 的单线程模型却能达到惊人的每秒 10 万级别的处理能力呢?(采用百度百科) 这就是 Redis 在设计上的优秀之处。 实现高性能的一个方面是 Redis 是基于内存操作,它内部高效的数据结构,如跳表([可以看看之前的文章,# 今天终于知道 Redis 为什么要用跳跃表了](https://juejin.cn/post/7149101822756519949))哈希表等,还有就是 Redis 采用了 I/O多路复用机制,从而保障在网络 I/O 中能够高效的处理并发请求,实现高吞吐率。这二者是实现高性能的重要原因。 接下来就说说本文最重要的一个 **I/O 多路复用**机制。 ### 3. I/O 多路复用模型 在 Linux 中,我们都知道 Linux 对文件的操作实际上就是通过文件描述符(fd),通过一个进程监听多个文件描述符,一旦某个文件描述符准备就绪,就会通知对应的程序响应并处理,这种通知的方式优势在于在**单个时间内能够处理更多的链接。** Linux 中的 I/O 多路复用机制是指一个线程处理多个 IO 流,也就是我们通常说的 select/epoll。 在 Redis 单线中,允许在内核中同时存在多个监听文件描述符,内核会去监听在这上面的链接请求,一但有请求就会交给 Redis 线程处理,从而实现一个Redis 线程可以处理多个 IO 流。那么什么是 select、epoll? #### 3.1 select **select** 是一个函数,它支持最大的连接数是 1024 或者 2048,因为在 select 函数需要传入fd_set 参数,这个 fd_set 的长度取决于操作系统的位数 1024 或者 2048。 其中 fd_set 是一个 bitmap,当数据没有到缓冲区那么就是 0, 反之到了缓冲区就是 1。select 函数的功能是将 fd_set 遍历,判断标识位是否存在变化,若发生变化就发起中断处理。 #### 3.2 epoll **epoll** 的首次提出是在 Linux 2.6 内核中,他是为了解决 select 的缺点。 它定义了 epoll_event 结构体来处理,解决 select 存在最大链接数的限制。epoll 不会遍历所有的文件描述符(fd),epoll 会将准备就绪的文件描述符维护在一块指定的空间内,每次从其中取出已经准备就绪的文件描述符进行处理,大大提高了性能。 这就是 select 和 epoll 的区别,想看具体的源码可以自行了解,这里只是简单的描述一下。 #### 3.3 Redis I/O 多路复用模型 在 Redis 中,其网络框架调用采用的是复用机制中的 epoll 机制,让内核监听文件描述符,此时 Redis 线程不会阻塞在某一个特定的监听或已连接的文件描述符,从而可以达到同时处理多个链接请求,提高并发性能。如下图,Redis I/O 多路复用模型: ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9872f81b6b07453ba5711cafacdf3948~tplv-k3u1fbpfcp-zoom-1.image) 为了当请求到达时会通知 Redis 线程, select/epoll 提供了基于事件的回调机制,即针对不同事件的发生,调用相应的处理函数。 说说回调机制时如何高效的工作的。当 select/epoll 监测到有文件描述符请求到达时,会发出对应的事件处理,这些事件会被放到一个事件处理队列中,然后 Redis 会对事件进行处理。通过对队列进行轮询,可以提高 CPU 利用率。同时,Redis 在处理事件时,会调用其相应的事件处理函数,实现基于事件的回调。最终使得请求能够第一时间及时响应,再一步提升 Redis 的相应性能。 举个发起读数据的例子,更好的理解上面 Redis I/O 多路复用模型。 当程序发起 Accept 和 Read 事件时, Redis 线程会注册这 Accept 和 Get 事件 对应的回调函数。当 Linux 内核监听到有链接请求或者读数据的请求时,会触发 Accept 和 Read 事件,与此同时调用 Redis 的 Accept 和 Get 函数进行数据处理。 ### 4. 总结 现在,我们知道啦,Redis 为什么快了吧。 Redis 单线程是指在网络 I/O 和 键值的读写操作是有一个线程来完成的,采用单线程的好处是避免了多线程并发需要竞争获取锁。单线程之所以性能高效是因为其选择了 I/O 多路复用模型。搞懂了 select/epoll 这这些,慢慢会发现自己会逐渐比身边的伙伴优秀一些。 好了,就到这,我是程序员祝融,欢迎关注,我们下期再见! ![微信.png](https://static.golangjob.cn/221209/6b82ffc1bbb877d55631b73171f3534b.png)

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1830 次点击  ∙  1 赞  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传