Redis的线程模型

作者:vivo互联网服务器团队-Wang Shaodong

来源:vivo互联网技术open in new window

概述

Redis服务器中有两类事件,文件事件和时间事件。

  • 文件事件:在这里可以把文件理解为Socket相关的事件,比如连接、读、写等;
  • 时间时间:可以理解为定时任务事件,比如一些定期的RDB持久化操作。

本文重点聊下Socket相关的事件。

模型图

首先我们来看下Redis服务的线程模型图;

IO多路复用负责各事件的监听(连接、读、写等),当有事件发生时,将对应事件放入队列中,由事件分发器根据事件类型来进行分发;

如果是连接事件,则分发至连接应答处理器;GET、SET等redis命令分发至命令请求处理器。

命令处理完后产生命令回复事件,再由事件队列,到事件分发器,到命令回复处理器,回复客户端响应。

一次客户端和服务端的交互流程

连接流程

连接过程

  • Redis服务端主线程监听固定端口,并将连接事件绑定连接应答处理器。
  • 客户端发起连接后,连接事件被触发,IO多路复用程序将连接事件包装好后丢人事件队列,然后由事件分发处理器分发给连接应答处理器。
  • 连接应答处理器创建client对象以及Socket对象,我们这里关注Socket对象,并产生ae_readable事件,和命令处理器关联,标识后续该Socket对可读事件感兴趣,也就是开始接收客户端的命令操作。
  • 当前过程都是由一个主线程负责处理。

命令执行流程

SET命令执行过程

  • 客户端发起SET命令,IO多路复用程序监听到该事件后(读事件),将数据包装成事件丢到事件队列中(事件在上个流程中绑定了命令请求处理器);
  • 事件分发处理器根据事件类型,将事件分发给对应的命令请求处理器;
  • 命令请求处理器,读取Socket中的数据,执行命令,然后产生ae_writable事件,并绑定命令回复处理器;
  • IO多路复用程序监听到写事件后,将数据包装成事件丢到事件队列中,事件分发处理器根据事件类型分发至命令回复处理器;
  • 命令回复处理器,将数据写入Socket中返回给客户端。

模型优缺点

以上流程分析我们可以看出Redis采用的是单线程Reactor模型,我们也分析了这种模式的优缺点,那Redis为什么还要采用这种模式呢?

Redis本身的特性

  • 命令执行基于内存操作,业务处理逻辑比较快,所以命令处理这一块单线程来做也能维持一个很高的性能。

优点

  • 让主线程专注于通用事件的处理(连接、读、写),从设计上进一步解耦;
  • 利用CPU多核的优势。

缺点

  • Reactor单线程模型的缺点也同样在Redis中来体现,唯一不同的地方就在于业务逻辑处理(命令执行)这块不是系统瓶颈点。
  • 随着流量的上涨,IO操作的的耗时会越来越明显(read操作,内核中读数据到应用程序。write操作,应用程序中的数据到内核),当达到一定阀值时系统的瓶颈就体现出来了。

Redis又是如何去解的呢?

哈哈~将耗时的点从主线程拎出来呗?那Redis的新版本是这么做的吗?我们一起来看下。

Redis多线程模式

Redis的多线程模型跟”多Reactor多线程模型“、“单Reactor多线程模型有点区别”,但同时用了两种Reactor模型的思想,具体如下;

  • Redis的多线程模型是将IO操作多线程化,本身逻辑处理过程(命令执行过程)依旧是单线程,借助了单Reactor思想,实现上又有所区分。
  • 将IO操作多线程化,又跟单Reactor衍生出多Reactor的思想一致,都是将IO操作从主线程中拎出来。

命令执行大致流程

  • 客户端发送请求命令,触发读就绪事件,服务端主线程将Socket(为了简化理解成本,统一用Socket来代表连接)放入一个队列,主线程不负责读;
  • IO 线程通过Socket读取客户端的请求命令,主线程忙轮询,等待所有 I/O 线程完成读取任务,IO线程只负责读不负责执行命令;
  • 主线程一次性执行所有命令,执行过程和单线程一样,然后需要返回的连接放入另外一个队列中,有IO线程来负责写出(主线程也会写);
  • 主线程忙轮询,等待所有 I/O 线程完成写出任务。
Last Updated: