7

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

 1 year ago
source link: https://studygolang.com/articles/35995
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

webff · 5天之前 · 730 次点击 · 预计阅读时间 3 分钟 · 大约8小时之前 开始浏览    

今天我们讨论一下 redis 面试高频题,为什么 Redis 那么快?
首先,你可以先想一下答案,我先说下大家普遍的答案:

  • 基于内存操作,速度快
  • I/O 多路复用

相信很多人第一时间回答出来上面这些,那么面试官一般会接着问,所有的操作都是单线程吗?单线程为什么快呢,什么是 I/O 多路复用?很多人这个时候就会 G 了。

今天我们好好聊聊,首先我们要知道,我们常说的 Redis 是单线程,主要是指 Redis 在网络 I/O 和 键值的读写操作是有一个线程来完成的,但是其他的一些功能是由其他的线程来执行的,如:

  • 持久化数据落盘
  • 集群主从同步
  • 异步数据删除

所以严格来说,Redis 并不是单线程的。但是在面试中,我们可以先铺垫一下,如果面试官刨根问底,显然是不够的。

1. Redis 为什么用单线程

日常开发中,我们接触都更多是多线程,为了能够提升系统的请求数或者是吞吐率,我们会采用多线程来实现。看下图:

9ecca87ba80449c5a07d9fa186f59a1e~tplv-k3u1fbpfcp-zoom-1.image

为啥会出现预期和实际结果不一样的情况?其实是当出现多线程同时在操作一个共享的资源时,我们为了保证结果的正确性,我们需要有额外的开销来保证,如锁。当有锁出现了,我们就需要在考虑在什么时候需要获取锁,释放锁,其中就需要记录锁的状态,可想而知,性能就会下降。这就是多线程模式在面临高并发场景下共享资源的访问问题。

所以大家应该知道为啥 Redis 要用单线程了吧,我们接着来说说为什么单线程的 Redis 为啥能获得高性能的原因。

2. 单线程 Redis 为什么那么快

我们正常理解下,多线程在处理能力上是要高于单线程的,但是为啥 Redis 的单线程模型却能达到惊人的每秒 10 万级别的处理能力呢?(采用百度百科)

这就是 Redis 在设计上的优秀之处。

实现高性能的一个方面是 Redis 是基于内存操作,它内部高效的数据结构,如跳表(可以看看之前的文章,# 今天终于知道 Redis 为什么要用跳跃表了)哈希表等,还有就是 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 多路复用模型:

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 函数进行数据处理。

现在,我们知道啦,Redis 为什么快了吧。 Redis 单线程是指在网络 I/O 和 键值的读写操作是有一个线程来完成的,采用单线程的好处是避免了多线程并发需要竞争获取锁。单线程之所以性能高效是因为其选择了 I/O 多路复用模型。搞懂了 select/epoll 这这些,慢慢会发现自己会逐渐比身边的伙伴优秀一些。

好了,就到这,我是程序员祝融,欢迎关注,我们下期再见!

微信.png


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

280

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK