如何兼职做网站,网页微信版的手机登录,淄博企业网站建设价格,gl账号注册网站Redis知识点总结#xff08;二#xff09;——Redis高性能IO模型及其事件驱动框架剖析 IO多路复用传统的阻塞式IO同步非阻塞IOIO多路复用机制 Redis的IO模型Redis的事件驱动框架 IO多路复用
Redis的高性能的秘密#xff0c;在于它底层使用了IO多路复用这种高性能的网络IO二——Redis高性能IO模型及其事件驱动框架剖析 IO多路复用传统的阻塞式IO同步非阻塞IOIO多路复用机制 Redis的IO模型Redis的事件驱动框架 IO多路复用
Redis的高性能的秘密在于它底层使用了IO多路复用这种高性能的网络IOIO多路复用是一种高性能的IO机制。
传统的阻塞式IO
传统的同步阻塞式IO一个IO连接对应一个线程也就是说一个线程只能监听一个IO连接并且在没有数据到达前这个线程会阻塞等待数据的到达在数据到达后才把数据读取到应用程序的内存区域。
比如我们熟悉的socket编程就是典型的同步阻塞式网络IOServerSocket的accept()方法监听指定端口接收客户端建立连接此时当前线程会阻塞等待客户端发起连接。建立连接后返回一个Socket表示与客户端建立了一个连接然后我们创建一个线程Thread在线程中调用Socket的read()方法等待客户端发送数据如果客户端迟迟不发送数据那么该线程将会一直被阻塞。 这是一种性能比较低的IO机制原因在于一个线程只能监听一个连接并且在数据没有到达之前需要阻塞等待。
同步非阻塞IO
而同步非阻塞式IO则不一样它可以尝试性的读取看看是否有数据如果没有数据可以立即返回不会阻塞当前线程可以去干点别的事情然后再次回来尝试读取如果发现有数据到达才会阻塞当前线程当前线程会把数据读取到应用程序用户空间。 在没有数据达到前当前线程不会阻塞因此叫非阻塞式IO而有数据达到时数据读取的工作是当前线程自己处理的异步IO不需要线程自己读取数据因此又叫同步IO合在一起就是同步非阻塞IO。
这种IO机制虽然不会阻塞当前线程但是不停尝试读取的做法非常消耗CPU资源于是就有了IO多路复用。
IO多路复用机制
IO多路复用最大的特点就是一个线程可以监听多个socket。在Linux操作系统里面提供了select、poll、epoll三种IO多路复用API由于Redis底层使用的时epoll我们就分析一下epoll这种IO多路复用机制。
epoll这种IO多路复用机制有三个函数分别是epoll_create、epoll_ctl、epoll_wait。epoll_create的作用是创建一个epoll实例这个epoll实例是一个IO多路复用器里面使用一个红黑树结构存储注册进来的socket文件描述符当某个socket有数据到达或有连接需要建立时又会把该socket复制到一个链表结构当中epoll_ctl则是把一个socket文件描述符注册到epoll实例当中epoll_wait则是获取epoll实例中已经有事件就绪的socket也就是epoll实例中的链表把该链表拷贝到用户空间当前线程就可以遍历该链表进行处理。 如果是建立连接事件则调用socket的accept()函数建立连接获取另一个socket把该socket注册到epoll实例中如果是有数据达到则调用socket的read()函数读取数据如果是有数据需要写出则调用socket的write()函数。 Redis的IO模型
redis基于IO多路复用进行封装就有了自己的IO模型。Redis的IO模型是单线程Reactor模型也就是事件监听reactor、建立建立连接acceptor、事件处理handler都由同一个线程负责。这里的reactor、acceptor、handler是IO模型中的三种角色三个角色可以由不同线程担当也可由同一个线程担当在单线程Reactor这种IO模型中显然三种角色都是由同一个线程担当。此时reactor表示事件监听的处理逻辑acceptor表示连接建立的处理逻辑handler表示处理读写事件的逻辑。 在单线程reactor这种IO模型下程序会启动一个主线程主线程启动时会调用epoll的epoll_create函数创建一个epoll实例并创建一个socket调用listen函数把它转成监听socket调epoll_ctl函数注册到epoll实例中。然后主线程会调用epoll中的epoll_wait函数监听注册到epoll实例中的所有socket一旦有事件就绪就会进行事件分派分派给acceptor或handler处理这就是reactor的逻辑 当注册到epoll实例中的socket有事件就绪epoll_wait函数就会返回当前线程就会遍历有事件就绪的socket根据事件类型进行事件分派。如果是建立连接事件就会调用acceptor的逻辑处理acceptor中的逻辑就是调用socket.accept()获取已建立连接的socket然后调用epoll_ctl把该socket注册到epoll实例如果是读写事件就会调用handler的逻辑处理读写事件读事件的处理就是调用socket.read()获取数据然后进行相应处理写事件就是调用socket.write()把数据写出。 Redis的事件驱动框架
基于这种单线程reactor的IO模型redis就封装出了自己的事件驱动框架。 整个Redis事件驱动框架的主体逻辑就是一个主线程的死循环在循环中当前线程调用epoll_wait进行监听获取有事件就绪的socket然后放入一个队列所有socket都放入队列后会遍历队列中的socket进行事件分派。如果是连接建立事件就会调用socket.accept()建立连接然后调用epoll_ctl函数把返回的socket注册到epoll实例中如果是读就绪事件就调用socket.read()函数读取客户端发送的数据也就是客户端发来的redis命令然后执行该redis命令对应的函数如果是写就绪事件则调用socket.write()函数把数据写出。
Redis的核心逻辑是单线程处理但不表示Redis真的就只有一个线程一些文件关闭、惰性删除、AOF文件刷盘、执行bgsave命令写内存快照等任务还是由后台线程来执行所以Redis并不是真正的单线程。 在Redis的6.0版本开始引入了多线程IO读写机制此时Redis的事件驱动框架在处理IO读写时会使用多线程的方式进行处理而核心逻辑也就是redis的命令处理还是由一个主线程执行。 Redis会把所有读就绪的socket以轮询的方式分配给所有IO线程处理IO线程会读取socket中的数据然后主线程等待所有的IO线程读取完毕再进行命令处理。处理完毕后需要把处理结果写回客户端时Redis再次进行分配把待写回数据的socket分配给IO线程进行数据写回。
这样即保持了Redis单线程处理的核心逻辑不变又通过多线程IO读写这种机制提升了IO读写的速度从而进一步提升Redis的性能。