【redis】线程IO模型

Redis线程IO模型

总结:在redis5.0及之前,redis线程io模型是单线程。那么Redis单线程如何处理那么多的并发客户端连接的?原因两点:1)非阻塞io 2)多路复用(事件轮询)

以下,我就针对上面的总结来展开说说redis线程io模型。

1、客户端与redis服务器的通信过程

当客户端执行redis.set(“key”,“value”)命令时,

  • 客户端通过操作系统创建一个套接字
    • 这个套接字会连接到运行 Redis 服务器的机器地址和端口(通常是 6379)。
    • 一旦连接建立成功,你的程序就可以通过这个套接字向 Redis 服务器发送数据
  • 客户端将命令通过 write() 进入 内核写缓冲区
  • 内核写缓存区的数据会从网卡 → redis服务端的内核读缓冲区。
  • redis服务端通过多路复用(epoll)得知内核读缓存区有命令需要执行。
  • 执行之后的响应数据进入Redis服务器的 内核写缓冲区
  • 接着内核写缓存区的数据又会从网卡 → redis客户端的内核读缓冲区。
  • 客户端通过 read() 获取响应数据。

当客户端执行redis.get(“key”)命令时,

  • 客户端将命令通过 write() 进入 内核写缓冲区
  • 内核写缓存区的数据会从网卡 → redis服务端的内核读缓冲区。
  • redis服务端通过多路复用(epoll)得知内核读缓存区有命令需要执行。
  • 执行之后的响应数据进入Redis服务器的 内核写缓冲区
  • 接着内核写缓存区的数据又会从网卡 → redis客户端的内核读缓冲区。
  • 客户端通过 read() 获取响应数据。

GET 命令全流程:

在这里插入图片描述

客户端详细视角:

在这里插入图片描述

服务端详细视角:

在这里插入图片描述

redis服务端视角就能体现Redis高性能的核心
通过非阻塞IO + 多路复用(epoll/kqueue)单线程监听所有连接的缓冲区状态

  • 当某连接的读缓冲区有数据 → 触发Redis读取命令
  • 当某连接的写缓冲区有空闲 → 触发Redis发送响应

此时你可能还不了解非阻塞IO和多路复用究竟是什么?没事,继续看下去。

这里要特别注意,**这里的非阻塞IO是指redis服务器,而非客户端。**因此下面将对比介绍阻塞IO和非阻塞IO,以及多路复用(事件循环)。

2、非阻塞IO & 阻塞IO

阻塞IO:典型的例子就是Java 的 Jedis(同步阻塞客户端)

jedis.get(“key”) :

  • 触发 write():客户端将 GET key 命令写入内核写缓冲区 → 通常瞬间完成

    • 缓冲区未满:写入数据
    • 缓冲区满了:线程卡在 write() 调用(阻塞)
  • 触发read():客户端尝试从内核读缓冲区读取 Redis 的响应(如 "value"

    • 有数据:立即返回结果 → 线程继续执行

    • 无数据:线程卡在 read() 调用(阻塞)

非阻塞IO:典型的例子就是Redis 服务器内部

Redis 将 socket 设置为 Non_Blocking

  • 调用 read() 时无数据,也会立刻返回 EAGAIN 错误而非阻塞。
  • 调用 write() 时无空间,也会立刻返回 EAGAIN 错误而非阻塞。

这边想要再扩展一个非阻塞IO的例子:

支持非阻塞的客户端库(如 Lettuce

  1. commands.get() 将命令写入内核写缓冲区(非阻塞写)。
  2. 客户端库 注册回调函数 并立即返回。
  3. 库内部用 Selector 轮询 内核读缓冲区 → 数据到达后调用回调。

其实,redis只有在感知到内核读缓冲区有数据时,才会调用 read() 去读取数据。

那么redis是怎么感知到内核读缓冲区有数据的?当缓冲区满了,又是怎么知道要什么时候能继续写入数据?答案就是通过多路复用(epoll)。

3、多路复用(epoll)

// Redis 事件循环伪代码
void eventLoop() {while(server.running) {// 0. 计算超时时间(动态值,假设为200ms)timeout = calculate_timeout(); // 1. 获取待处理事件(核心:epoll_wait 在此等待,最多只会阻塞等待timeout时间,如果在这个超时时间内有数据了就会恢复运行态,如果在这个超时时间内依然没有数据,那么就会去处理其他事件,比如时间事件)events = epoll_wait(epoll_fd, events, MAX_EVENTS, timeout); // 2. 处理文件事件(网络请求,处理命令)for each event in events:if event.is_readable:  // 可读事件 → 执行客户端命令readQueryFromClient()if event.is_writable: // 可写事件 → 发送响应writeReplyToClient()// 3. 处理时间事件(定时任务,如RDB备份、Key过期)processTimeEvents()}
}

流程图如下:

 />

以上代码,基本可以说明redis单线程都在干什么了:

一个永不停止的循环(while(1)),用 单线程 同时监听 所有客户端连接 + 定时任务 + 内部任务,通过事件分发处理请求。

这里具体介绍epoll机制:

// Redis 仅通过 epoll 做一件事:
int num_events = epoll_wait(epoll_fd, events, MAX_EVENTS, timeout);
  • 监听对象:所有客户端连接的 socket
  • 监听事件
    • EPOLLIN内核读缓冲区有数据可读(客户端命令到达)
    • EPOLLOUT内核写缓冲区有空闲空间(可发送响应)

在这里插入图片描述

epoll 感知事件的底层原理是全程无CPU轮询!依赖硬件中断

// 系统调用流程
int epoll_wait() {// 1. 检查就绪队列(快速路径)if (!list_empty(rdllist)) return events; // 立即返回// 2. 无事件时:让出CPUset_current_state(TASK_INTERRUPTIBLE); // 标记为阻塞态schedule(); // 主动让出CPU → 其他进程运行
}

也就是说,进入epoll_wait方法,该redis单线程是处于阻塞态的,不占cpu的任何消耗。

在这里插入图片描述

这里我还想补充一点,就是之前我学的,redis的rdb持久化以及aof重写过程中会fork一个子进程、aof是开启后台线程刷屏、以及redis4.0出现的懒惰删除也是在后台线程进行的。以上三种情况与redis的单线程又是什么关系呢?

可以这样理解:Redis的「单线程」本质上是对命令执行模型的核心描述,但整个系统确实存在多线程/多进程协作。

在这里插入图片描述

BIO后台线程(Background I/O Threads):

  • AOF fsync :将AOF缓冲数据刷盘,因为 fsync() 可能阻塞30ms+
  • lazy free :异步删除大Key,避免主线程阻塞数秒。(BIO线程删除Key时,主线程已将该Key标记为逻辑删除(移除key的指针的指针引用),BIO只是物理释放内存)

在这里插入图片描述

子进程(Child Process):

  • AOF重写 触发 BGREWRITEAOF,fork子进程,父子进程共享内存页,Copy-On-Write 写时复制
  • RDB持久化 执行 SAVE / 定时保存,fork子进程,父子进程共享内存页,Copy-On-Write 写时复制

总结:主线程独占写操作(100%),而子进程是只读数据,而BIO线程只处理非数据操作。

  • AOF重写 触发 BGREWRITEAOF,fork子进程,父子进程共享内存页,Copy-On-Write 写时复制
  • RDB持久化 执行 SAVE / 定时保存,fork子进程,父子进程共享内存页,Copy-On-Write 写时复制

总结:主线程独占写操作(100%),而子进程是只读数据,而BIO线程只处理非数据操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/diannao/86780.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

进程间通信详解(三):Linux进程信号深度解析

文章目录 一、Linux进程信号核心概念1.1 信号本质1.2 关键术语1.3 Linux 信号机制的核心流程: 二、信号产生机制全景2.1 通过终端按键产生信号2.1.1 基本操作 2.2 调用系统命令向进程发信号2.2.1 kill 命令:向指定进程发送信号2.2.2 killall 命令&#x…

C++ 日志系统实战第五步:日志器的设计

全是通俗易懂的讲解,如果你本节之前的知识都掌握清楚,那就速速来看我的项目笔记吧~ 本文项目代码编写收尾! 日志器类 (Logger) 设计(建造者模式) 日志器主要用于和前端交互。当我们需要使用日志系统打印 log 时&…

Spring Boot + MyBatis日志前缀清除方法

在 Spring Boot 结合 MyBatis 的应用中&#xff0c;清空日志前缀&#xff08;如 > 、< 等&#xff09;需要通过 自定义 MyBatis 的日志实现 或 修改日志模板 来实现。以下是两种常用方法&#xff1a; 方法 1&#xff1a;自定义 MyBatis 日志实现&#xff08;推荐&#xf…

【消息队列】——如何实现消息保序

目录 一、哪些场景需要消息保序?二、如何实现消息保序?三、保序消息的常见问题和应对策略3.1、重复消息3.2、节点故障3.3、分区扩容四、小结本文来源:极客时间vip课程笔记 一、哪些场景需要消息保序? 消息保序问题指的是,在通过消息中间件传递消息过程中,我们希望消费者收…

Transformer模型详解

Transformer Transformer真是个细节满满的框架呢&#xff0c;大三读到根本不敢看&#xff0c;考研复试前看了看&#xff0c;以为懂了其实差得还远&#xff0c;两个多月前看了&#xff0c;还是一知半解&#xff0c;如今终于经过细细分析&#xff0c;算是知道了Transformer的基本…

火山引擎发布豆包大模型 1.6 与视频生成模型 Seedance 1.0 pro

6 月 11 日&#xff0c;在火山引擎 FORCE 原动力大会上&#xff0c;字节跳动旗下火山引擎正式发布豆包大模型 1.6、豆包・视频生成模型 Seedance 1.0 pro、豆包・语音播客模型&#xff0c;豆包・实时语音模型也在火山引擎全量上线&#xff0c;豆包大模型家族已成为拥有全模态、…

PH热榜 | 2025-06-12

1. Atlas 标语&#xff1a;几秒钟内了解定价情况 介绍&#xff1a;获取即插即用的定价页面&#xff0c;让你轻松赚钱&#xff0c;不再辛苦操劳。 产品网站&#xff1a; 立即访问 Product Hunt&#xff1a; View on Product Hunt 关键词&#xff1a;Atlas, 定价快速, 插件式…

ChatGPT革命升级!o3-pro模型重磅发布:开启AI推理新纪元

2025年6月10日&#xff0c;OpenAI以一场低调而震撼的发布&#xff0c;正式推出了新一代推理模型o3-pro&#xff0c;这标志着人工智能在复杂问题解决领域的重大突破。作为ChatGPT Pro和Team订阅用户的专属工具&#xff0c;o3-pro不仅重新定义了AI的可靠性标准&#xff0c;更以其…

NVIDIA Isaac GR00T N1.5 适用于 LeRobot SO-101 机械臂

系列文章目录 目录 系列文章目录 前言 一、简介 二、详细教程 2.1 数据集准备 2.1.1 创建或下载您的数据集 2.1.2 配置模态文件 2.2 模型微调 2.3 开环评估 2.4 部署 &#x1f389; 快乐编程&#xff01;&#x1f4bb;&#x1f6e0;️ 立即开始&#xff01; 前言 一…

【编译工具】(自动化)自动化测试工具:如何让我的开发效率提升300%并保证代码质量?

目录 引言&#xff1a;自动化测试在现代开发中的关键作用 一、自动化测试金字塔&#xff1a;构建高效的测试策略 &#xff08;1&#xff09;测试金字塔模型 &#xff08;2&#xff09;各层级代表工具 二、前端自动化测试实战&#xff1a;Jest Cypress &#xff08;1&…

R语言缓释制剂QBD解决方案之一

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》缓释制剂包衣处方研究的R语言解决方案。 ER聚合物包衣处方优化研究 基于初步风险评估和初始可行性研究&#xff0c;进行带3个中心点的24-1分式析因DOE。药物的释放被识别为CQA。本研究的…

行为模式-命令模式

定义&#xff1a; 命令模式是一个高内聚的模式&#xff0c;其定义为&#xff1a;Encapsulate a request as an object,thereby letting you parameterize clients with different requests,queue or log requests,and support undoable operations.&#xff08;将一个请求封装成…

Ubuntu 24.04 上安装与 Docker 部署 Sentinel

Ubuntu 24.04 上安装与 Docker 部署 Sentinel 一、Sentinel 简介 Sentinel 是阿里巴巴开源的分布式系统流量控制组件&#xff0c;提供流量控制、熔断降级和系统负载保护等功能。它通过可视化控制台&#xff08;Dashboard&#xff09;实现实时监控和规则管理&#xff0c;是微服…

IP 地址查询在证券交易中的应用方式

网络安全保障与IP地址查询 证券交易平台存储着海量投资者的敏感信息以及巨额资金的交易数据&#xff0c;是网络攻击的重点目标。IP 地址查询在检测异常登录行为方面至关重要。例如&#xff0c;当一个账户短时间内先在国内某城市登录&#xff0c;随后又在境外 IP 地址发起交易操…

Flutter 常用组件详解:Text、Button、Image、ListView 和 GridView

Flutter 作为 Google 推出的跨平台 UI 框架&#xff0c;凭借其高效的渲染性能和丰富的组件库&#xff0c;已经成为移动应用开发的热门选择。本文将深入探讨 Flutter 中最常用的五个基础组件&#xff1a;Text、Button、Image、ListView 和 GridView&#xff0c;帮助开发者快速掌…

docker 单机部署redis集群(一)

docker 部署redis集群 1、创建redis网卡 docker network create redis --subnet 172.38.0.0/16查看网卡信息 docker network ls docker network inspect redis2、创建redis配置 #使用脚本创建6个redis配置for port in $(seq

MySQL 索引学习笔记

1.二叉树&#xff0c;红黑树&#xff0c;B 树&#xff0c;B树 二叉树&#xff1a;就是每个节点最多只能有两个子节点的树&#xff1b; 红黑树&#xff1a;就是自平衡二叉搜索树&#xff0c;红黑树通过一下五个规则构建&#xff1a; 1.节点只能是红色或黑色&#xff1b; 2.根…

Windows安装docker及使用

下载 https://www.docker.com/ 安装 启动 此时拉取镜像会报错 Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers) 配置引擎 添加以…

多参表达式Hive UDF

支持的操作符 &#xff1a;跳过&#xff0c;即无条件筛选&#xff1a;等于!&#xff1a;不等于range&#xff1a;区间内&#xff0c;range[n,m]表示 between n and mnrange&#xff1a;区间外&#xff0c;即not between andin&#xff1a;集合内&#xff0c;in(n,m,j,k)表示 in…

GO后端开发内存管理及参考答案

什么是 Go 的逃逸分析&#xff08;Escape Analysis&#xff09;&#xff0c;为什么需要它&#xff1f; Go 的逃逸分析是一种编译时技术&#xff0c;用于确定变量的生命周期是否超出其创建的函数作用域。通过分析变量的使用方式&#xff0c;编译器能够判断变量是否需要在堆上分…