Java 面试宝典:Redis 的线程模型是怎么样的?

大家好,我是大明哥,一个专注「死磕 Java」系列创作的硬核程序员。 本文已收录到我的技术网站:www.skjava.com。有全网最优质的系列文章、Java 全栈技术文档以及大厂完整面经


Redis 的线程模型其实是分两块的:

  1. Redis 6.0 之前的单线程模型。其实从 4.0 开始,Redis 并不是严格意义上的单线程模型,因为 Redis 除了主线程外,也有一些后台的线程或者子进程在处理任务(例如清理脏数据、生成快照、AOF 重写),这个时候大家所说的单线程应该是 Redis 的主线程模型。
  2. Redis 6.0 之后的多线程模型。Redis 在 6.0 之后引入了一种多线程模型,用于处理网络 I/O 的任务。

所以,你的回答要涉及这两个方面。

  • Redis 的单线程是指Redis 在执行一次命令时是单线程的。其过程包括「接收客户端请求 -> 解析请求 ->数据读写等操作->返回结果给客户端」,这个过程是由一个主线程来完成的,这也是我们常说 Redis 是单线程的原因。Redis 的模型是基于单线程事件驱动模型,内部使用文件事件处理器,而这个文件事件处理是单线程的,也就决定了 Redis 是单线程的。其核心原理是:采用 IO多路复用机制同时监听多个 socket,将产生事件的 socket 压入内存队列中,事件分派器根据 socket 上的事件类型来选择对应的事件处理器进行处理。
  • 随着底层网络硬件越来越好,Redis 的性能瓶颈逐渐体现在网络 I/O 的读写上,单个线程处理网络 I/O 读写的速度跟不上底层网络硬件执行的速度。所以为了提高 Redis 的性能,在 Redis 6.0 引入多线程模型,该多线程模型只用来处理网络数据的读写和协议解析,执行读写命令的仍然是单线程。

Redis 线程模型详解

Redis 的单线程模型

Redis 的单线程是指Redis 在执行一次命令时是单线程的。Redis 客户端与服务端的模型可以简化如下图:

步骤2 执行命令为单线程,其过程包括「接收客户端请求 -> 解析请求 ->数据读写等操作->返回结果给客户端」,这个过程是由一个主线程来完成的,这也是我们常说 Redis 是单线程的原因。

从 Redis 的内部设计来说,Redis 是基于 Reactor 模式开发了自己的网络事件处理器,这个处理器称之为文件事件处理器,而这个文件事件处理器是单线程的,这就决定了 Redis 是单线程的。文件事件处理器包含 5 个部分:

  • 多个 socket:Redis 网络通信的起点,Redis 服务器为每个连接的客户端维护一个套接字,用于接收请求和发送响应。
  • IO 多路复用程序:文件事件处理器的核心。它负责监控所有套接字并确定哪些套接字准备好进行读写操作。
  • 任务队列:处理的任务的队列。
  • 文件事件分派器:当 I/O 多路复用程序确定某个套接字准备好读写时,文件事件分派器负责将这个事件分派给相应的事件处理器。
  • 事件处理器:Redis 对不同类型的文件事件定义了相应的事件处理器。当特定类型的事件发生时,对应的事件处理器会被触发以处理这些事件。

多个 socket 会产生不同的操作,每个操作对应一个不同的文件事件, IO 多路复用程序会监听多个 socket,将产生的事件放入到任务队列中排队,文件事件分派器每次从任务队列中获取一个事件,将其转发给对应的事件处理器进行处理。如下:

客户端与 Redis 服务端建立连接的过程

  • Redis sever 启动时,会把 AE_READABLE 事件与连接应答处理器关联。
  • 当客户端向 Redis 发起连接时,这是 Server Socket 会产生一个 AE_READABLE 事件,IO 多路复用程序监听到该事件后将 socket信息压入到任务队列中。
  • 文件事件分派器每次从任务队列中取一个 socket ,将其交给事件处理器,由于在 Redis 初始化时 AE_READABLE 是与连接应答处理器关联,所以就由连接应答处理器来处理该事件。
  • 连接应答处理器会创建一个与该客户端通信的 Socket(我们这里叫 socket1),并将 socket1 的 AE_READABLE 事件与命令请求处理器关联。

客户端发送请求给 Redis 服务端过程

  • 客户端发送读写请求(比如 set key value)给服务端,首先会在对应的 Socket(socket1)上面产生一个 AE_READABLE事件,IO 多路复用程序监听到该事件后将 socket信息压入到任务队列中。
  • 文件事件分派器从任务队列中取 Socket 信息转发给事件处理器,由于建立连接时 socket1 的 AE_READABLE 事件已经与命令请求处理器关联了,所以文件事件分派器将命令请求处理器。
  • 命令处理器读取该 Socket 的相关信息后执行相关命令,操作完成后,会将 socket01 的 AE_WRITABLE 事件与命令回复处理器关联。
  • 如果客户端已经准备好了接收结果,那么 socket1 会产生一个 AE_WRITABLE,IO 多路复用程序将 Socket 压入队列,然后由文件事件派发器转发给事件处理器。
  • 由于 socket1 的AE_WRITABLE 事件与命令回复处理器关联,所以由命令回复处理器处理,命令回复处理器将准备好的相应数据写入socket01(socket连接是双向的),返回给客户端,之后解除 socket01 的 AE_WRITABLE 事件与命令回复处理器的关联。

Redis 的 I/O 多线程模型

我们 Redis 是基于内存操作,内存的响应时长大约为 100 纳秒,单线程的 Redis 处理数据的极限是 80,000 到 100,000 QPS,对于绝大多数的场景来说,单线程的 Redis 其实是已经够用了。

但是,随着底层网络硬件越来越好,Redis 的性能瓶颈逐渐体现在 I/O 的读写上(CPU 从来都不是 Redis 的性能瓶颈),单个线程处理网络 I/O 读写的速度跟不上底层网络硬件执行的速度。所以,为了提高 Redis 的整体性能,在 6.0 引入多线程,注意,引入的多线程模型只⽤来处理处理网络数据的读写和协议解析,对于 Redis 的读写命令,依然是单线程处理

Redis 6.0 引入 I/O 多线程模型后,将一个命令的执行分为了两部分:

  • Socket 读写和请求解析使用多线程处理,多个 socket 读写可以并⾏化
  • 执行请求依然还是使用主线程,存内存操作,在高效的同时也保证了安全性。

主要流程如下:

  1. 主线程负责接收并建立(多个)连接请求,获取 socket 后放入全局等待处理队列;
  2. 主线程处理完这些事件之后,通过RR(Round Robin 轮询)将可读 socket 分配给这些 IO 线程;
  3. 主线程阻塞,等待 IO 线程完成命令的读取、解析;
  4. 主线程执⾏ IO 线程读取和解析出来的 Redis 请求命令,并将结果写到输出缓冲区;
  5. 主线程阻塞,等待 IO 线程将命令执⾏结果写回 socket(客户端);
  6. 主线程执行所有命令并清空整个等待队列,等待客户端后续的请求队列。

如下图:

相关推荐
AI人工智能+电脑小能手4 小时前
【大白话说Java面试题 第87题】【Mysql篇】第17题:分布式事务的实现原理?
java·数据库·分布式·mysql·面试
红尘散仙4 小时前
我把终端小说阅读器接上了 AI Agent:TRNovel 现在能用 skill 生成书源了
人工智能·后端·rust
卷毛的技术笔记6 小时前
告别硬编码!Spring AI Alibaba 实现 AI Agent 智能工具调用(Tool Calling)
java·人工智能·后端·python·spring·ai编程
会编程的土豆6 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
Cosolar6 小时前
从零写一个 Attention Is All You Need
人工智能·面试·架构
喵个咪6 小时前
GoWind Toolkit Go后端代码生成 完整全流程实战
后端·go·orm
basketball6167 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
qq_2518364577 小时前
SpringBoot+Vue 共享电池柜管理系统 完整实现 前后端分离项目实战 完整代码
vue.js·spring boot·后端
zhangxingchao7 小时前
AI 大模型核心六:量化、Workflow 与 Agent、多轮 RAG
前端·人工智能·后端
IT_陈寒8 小时前
Vite打包时遇到的坑,原来问题出在这里
前端·人工智能·后端