8、Redis 的线程模型、I/O 模型和多线程

Redis 的线程模型、I/O 模型和多线程

1. Redis 的线程模型

Redis 以其高效的单线程模型著称,从设计之初,Redis 就选择了单线程模式,这在很大程度上简化了其内部实现和维护。单线程模式避免了多线程编程中常见的竞争条件和锁机制问题,从而确保了 Redis 的高性能和稳定性。

单线程模型的优点

  1. 简化编程:没有多线程竞争的问题,无需使用复杂的锁机制。
  2. 高效利用 CPU:避免了线程切换和上下文切换的开销。
  3. 快速响应:在大多数情况下,单线程能够以极快的速度处理请求。

单线程模型的缺点

  1. CPU 利用率:单线程模式下,Redis 只能利用一个 CPU 核心,无法充分利用多核 CPU 的性能。
  2. I/O 阻塞:单线程模式容易在 I/O 操作上阻塞,影响整体性能。
2. Redis 的 I/O 模型

Redis 使用 Reactor 模式处理 I/O 操作。Reactor 模式是一种事件驱动的设计模式,通过非阻塞 I/O 和事件通知机制,实现高效的网络通信。

什么是 Reactor 模式?

Reactor 模式中的 "反应" 一词源自 "倒置" 和 "控制逆转"。具体事件处理程序不直接调用反应器,而是向反应器注册一个事件处理器,表示自己对某些事件感兴趣。当事件发生时,反应器调用事件处理器对事件做出反应。这种控制逆转也称为 "好莱坞法则"(不要调用我,让我来调用你)。

例如:

路人甲去做男士 SPA,前台的接待小姐接待了路人甲。路人甲对 10000 号技师感兴趣,告诉接待小姐,当 10000 号技师上班或空闲时通知他。接到通知后,路人甲做出反应,占用了 10000 号技师。

然后,路人甲想要 10000 号房间,告诉接待小姐,当房间空闲时通知他。接到通知后,路人甲再次做出反应。

在这个例子中,路人甲是具体事件处理程序,前台接待小姐是反应器,技师上班和房间空闲是事件。接待小姐不仅服务路人甲,还可以同时服务其他人,每个人的事件不一样。

单线程 Reactor 模式流程

服务器端的 Reactor 是一个线程对象,启动事件循环,并使用 Acceptor 事件处理器关注 ACCEPT 事件,监听客户端连接请求。客户端发起连接请求,Reactor 监听到 ACCEPT 事件,将其派发给 Acceptor 处理器处理。建立连接后,Reactor 监听 READ 事件。当 Reactor 监听到 READ 事件,将事件派发给处理器处理,读取数据。

在单线程 Reactor 模式中,不仅 I/O 操作在该线程上,非 I/O 业务操作也在该线程上,可能大大延迟 I/O 请求的响应。因此,应将非 I/O 业务逻辑操作从 Reactor 线程上卸载,加速 I/O 请求响应。

单线程 Reactor,工作者线程池

与单线程 Reactor 模式不同,添加了工作者线程池,将非 I/O 操作转交给线程池执行,提高 I/O 响应。对于小容量应用场景,单线程模型适用,但对于高负载、大并发、大数据量应用场景不合适,原因如下:

  1. 一个 NIO 线程同时处理成百上千的链路,性能无法支撑。
  2. 当 NIO 线程负载过重,处理速度变慢,导致大量客户端连接超时和消息积压,成为系统瓶颈。

多 Reactor 线程模式

Reactor 线程池中的每个 Reactor 线程都有自己的 Selector、线程和事件循环逻辑。mainReactor 可以只有一个,但 subReactor 一般有多个。mainReactor 负责接收客户端连接请求,将 SocketChannel 传递给 subReactor 完成与客户端的通信。

多 Reactor 线程模式将 "接受客户端连接请求" 和 "与客户端通信" 分开,由两个 Reactor 线程完成。mainReactor 接收连接请求,不负责通信,将连接转交给 subReactor 线程。这样,read() 数据量大时,不会影响后续客户端连接请求的即时处理。多 Reactor 线程模式在海量客户端并发请求下,通过实现 subReactor 线程池,将连接分发给多个 subReactor 线程,大大提升负载和吞吐量。

3. Redis 中的线程和 I/O 概述

Redis 基于 Reactor 模式开发了网络事件处理器 - 文件事件处理器(file event handler,简称 FEH)。该处理器是单线程的,所以 Redis 设计为单线程模型。通过 I/O 多路复用同时监听多个 socket,根据 socket 当前执行的事件选择对应的事件处理器。当被监听的 socket 准备好执行 accept、read、write、close 等操作时,FEH 调用 socket 关联的事件处理器处理事件。

虽然 FEH 是单线程运行,但通过 I/O 多路复用监听多个 socket,实现高性能的网络通信模型,并与 Redis 其他同样单线程运行的模块交互,保证 Redis 内部单线程模型的简洁设计。

文件事件处理器的组成部分

  1. Socket

    文件事件就是对 socket 操作的抽象,每当一个 socket 准备好执行连接 accept、read、write、close 等操作时,就会产生一个文件事件。一个服务器通常会连接多个 socket,多个 socket 可能并发产生不同操作,每个操作对应不同文件事件。

  2. I/O 多路复用程序

    I/O 多路复用程序负责监听多个 socket。尽管文件事件可能并发出现,但 I/O 多路复用程序将所有产生事件的 socket 放入队列,以有序、同步且每次一个 socket 的方式向文件事件分派器传送 socket。当上一个 socket 产生的事件被事件处理器执行完后,I/O 多路复用程序才会向文件事件分派器传送下一个 socket。

  3. I/O 多路复用程序的实现

    Redis 的 I/O 多路复用程序功能通过包装常见的 select、epoll、evport 和 kqueue 这些 I/O 多路复用函数库实现。每个函数库在 Redis 源码中对应一个单独文件。Redis 为每个 I/O 多路复用函数库实现了相同的 API,底层实现可互换。在 ae.c 文件中宏定义了相应规则,使程序在编译时自动选择系统中性能最高的 I/O 多路复用函数库作为底层实现。

    • evport = Solaris 10
    • epoll = Linux
    • kqueue = OS X,FreeBSD
    • select = 通常作为 fallback 安装在所有平台上

    Evport、Epoll 和 KQueue 具有 O(1) 复杂度,使用内核空间内存结构,可提供很多文件描述符。select 最多提供 1024 个描述符,对描述符完全扫描,复杂性为 O(n)。

  4. 文件事件分派器

    文件事件分派器接收 I/O 多路复用程序传来的 socket,根据事件类型调用相应事件处理器。

  5. 文件事件处理器

    服务器为执行不同任务的套接字关联不同事件处理器,这些处理器定义了事件发生时的动作。Redis 为各种文件事件需求编写了多个处理器,如连接应答处理器、命令请求处理器、命令回复处理器、复制处理器等。

  6. 文件事件类型

    I/O 多路复用程序可监听多个 socket 的 AE_READABLE 和 AE_WRITABLE 事件。当 socket 可读或有新的可应答 socket 出现时,产生 AE_READABLE 事件。当 socket 可写时,产生 AE_WRITABLE 事件。I/O 多路复用程序可同时监听 AE_READABLE 和 AE_WRITABLE 事件,如同时产生,优先处理 AE_READABLE 事件。

总结

客户端和 Redis 服务器通信过程如下:

  • Redis 启动初始化,将连接应答处理器与 AE_READABLE 事件关联。
  • 客户端发起连接,产生 AE_READABLE 事件,由连接应答处理器建立连接,创建客户端 socket,将 AE_READABLE 事件与命令请求处理器关联。
  • 客户端向 Redis 发送请求时,socket 产生 AE_READABLE 事件,触发命令请求处理器,读取命令内容,传给相关程序执行。
  • Redis 服务器准备好响应数据后,将 socket 的 AE_WRITABLE 事件与命令回复处理器关联。当客户端准备好读取响应数据时,socket 产生 AE_WRITABLE 事件,由命令回复处理器处理,将响应数据写入 socket,供客户端读取。
  • 命令回复处理器写完数据后,

删除 socket 的 AE_WRITABLE 事件与命令回复处理器的映射。

4. Redis 6.0 的多线程

随着 Redis 的发展,尤其是 Redis 6.0 的发布,引入了多线程模型,以提高在高并发场景下的性能。多线程的引入,旨在优化网络 I/O 操作,使 Redis 能够更好地利用多核 CPU 的性能。

Redis 6.0 之前的版本真的是单线程吗?

Redis 在处理客户端请求时,包括获取 (socket 读)、解析、执行、内容返回 (socket 写) 等都由一个顺序串行的主线程处理,这就是所谓的“单线程”。但从 Redis 4.0 之后,除了主线程外,还有后台线程处理一些较为缓慢的操作,如清理脏数据、无用连接的释放、大 key 的删除等。

为什么 Redis 6.0 之前一直不使用多线程?

官方曾解释:使用 Redis 时,几乎不存在 CPU 成为瓶颈的情况,主要受限于内存和网络。例如在普通的 Linux 系统上,Redis 通过 pipelining 每秒可处理 100 万个请求。如果应用程序主要使用 O(N) 或 O(log(N)) 的命令,它几乎不会占用太多 CPU。使用单线程后,可维护性高。多线程模型虽然在某些方面表现优异,但引入了程序执行顺序的不确定性,带来了并发读写问题,增加了系统复杂度,存在线程切换、加锁解锁、死锁造成的性能损耗。Redis 通过 AE 事件模型及 I/O 多路复用等技术,处理性能非常高,无需使用多线程。单线程机制使 Redis 内部实现的复杂度大大降低,“线程不安全”的命令可无锁进行。

为什么 Redis 6.0 要引入多线程?

随着业务场景复杂度增加,有些公司有上亿的交易量,需要更大的 QPS。常见解决方案是在分布式架构中对数据分区并采用多个服务器,但有缺点:管理的 Redis 服务器太多,维护代价大;单个 Redis 服务器的命令不适用于数据分区;数据分区无法解决热点读/写问题;数据偏斜,重新分配和放大/缩小变得复杂。

从 Redis 自身角度来说,读写网络的 read/write 系统调用占用了 Redis 执行期间大部分 CPU 时间,瓶颈主要在网络 I/O 消耗。优化有两个方向:

  1. 提高网络 I/O 性能,如使用 DPDK 替代内核网络栈。
  2. 使用多线程充分利用多核 CPU,典型实现如 Memcached。

协议栈优化方式与 Redis 关系不大,支持多线程是最有效便捷的操作方式。总结:Redis 支持多线程主要是为了充分利用 CPU 资源,分摊同步 I/O 读写负荷。

Redis 6.0 默认是否开启多线程?

Redis 6.0 的多线程默认禁用,只使用主线程。如需开启需修改 redis.conf 配置文件:io-threads-do-reads yes。开启多线程后,还需设置线程数,否则不生效。官方建议:4 核机器设置 2 或 3 个线程,8 核设置 6 个线程,线程数一定小于机器核数。线程数并非越大越好,超过 8 个基本无意义。

Redis 6.0 采用多线程后,性能提升效果如何?

Redis 作者 antirez 在 RedisConf 2019 分享时提到:Redis 6 引入多线程 I/O 特性,性能提升至少一倍以上。国内测试表明,GET/SET 命令在 4 线程 I/O 时性能比单线程几乎翻倍。如果开启多线程,至少需 4 核机器,且 Redis 实例占用较大 CPU 时才建议使用,否则无意义。

Redis 6.0 多线程实现机制

流程简述如下:

  1. 主线程负责接收连接请求,获取 socket 放入全局等待读队列。
  2. 主线程处理完读事件后,通过 RR (Round Robin) 将连接分配给 I/O 线程。
  3. 主线程阻塞等待 I/O 线程读取 socket 完毕。
  4. 主线程通过单线程方式执行请求命令,读取并解析数据,但不执行回写 socket。
  5. 主线程阻塞等待 I/O 线程将数据回写 socket 完毕。
  6. 解除绑定,清空等待队列。

该设计特点:

  1. I/O 线程要么同时在读 socket,要么同时在写,不会同时读或写。
  2. I/O 线程只负责读写 socket 解析命令,不负责命令处理。

开启多线程后,是否存在线程并发安全问题?

从实现机制看,Redis 多线程部分仅处理网络数据读写和协议解析,命令执行仍为单线程顺序执行。因此,无需考虑 key、lua、事务,LPUSH/LPOP 等并发及线程安全问题。

5. Redis 与 Memcached 多线程模型对比

相同点

  1. 主从模型:都采用主线程和工作线程模型。
  2. 线程分工:主线程处理连接,工作线程处理数据读写。

不同点

  1. Memcached:工作线程负责所有命令解析和执行,实现真正的线程隔离。
  2. Redis:主线程负责命令解析和执行,I/O 线程只负责网络数据读写,避免了线程安全问题。
6. 实践案例

高并发场景应用

在电商、金融等高并发场景下,Redis 6.0 的多线程模型显著提高系统响应速度和处理能力。例如,在秒杀活动中,Redis 处理大量订单请求,确保系统稳定性和高效性。

日志处理系统

在日志处理系统中,大量日志数据需实时写入和读取。Redis 6.0 的多线程模型可有效分担网络 I/O 负载,确保日志数据高效处理和存储。

实时监控系统

在实时监控系统中,海量监控数据需实时处理和展示。通过 Redis 6.0 的多线程模型,可大幅提升系统吞吐量和响应速度,确保监控数据及时性和准确性。

7. 结论

Redis 的线程模型和 I/O 模型是其高性能的关键。从最初的单线程模型到 6.0 引入的多线程模型,Redis 在保持高效的同时,不断优化以适应复杂的业务场景。多线程模型通过分摊网络 I/O 操作,提高了 Redis 的性能和扩展性。在高并发、高吞吐量的应用场景中,Redis 6.0 的多线程模型展示了强大的优势和应用潜力。

希望本文能够帮助读者深入理解 Redis 的线程模型、I/O 模型和多线程机制,为实际项目提供有力支持和指导。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【WebRTC实现点对点视频通话】

介绍 WebRTC (Web Real-Time Communications) 是一个实时通讯技术,也是实时音视频技术的标准和框架。简单来说WebRTC是一个集大成的实时音视频技术集,包含了各种客户端api、音视频编/解码lib、流媒体传输协议、回声消除、安全传输等。对于开发者来说可以…

【云原生】Prometheus监控Docker指标并接入Grafana

目录 一、前言 二、docker监控概述 2.1 docker常用监控指标 2.2 docker常用监控工具 三、CAdvisor概述 3.1 CAdvisor是什么 3.2 CAdvisor功能特点 3.3 CAdvisor使用场景 四、CAdvisor对接Prometheus与Grafana 4.1 环境准备 4.2 docker部署CAdvisor 4.2.2 docker部署…

汉诺塔与青蛙跳台阶

1.汉诺塔 根据汉诺塔 - 维基百科 介绍 1.1 背景 最早发明这个问题的人是法国数学家爱德华卢卡斯。 传说越南河内某间寺院有三根银棒,上串 64 个金盘。寺院里的僧侣依照一个古老的预言,以上述规则移动这些盘子;预言说当这些盘子移动完毕&am…

Java项目:基于SSM框架实现的共享客栈管理系统分前后台【ssm+B/S架构+源码+数据库+毕业论文】

一、项目简介 本项目是一套基于SSM框架实现的共享客栈管理系统 包含:项目源码、数据库脚本等,该项目附带全部源码可作为毕设使用。 项目都经过严格调试,eclipse或者idea 确保可以运行! 该系统功能完善、界面美观、操作简单、功能…

网页生成二维码、在线演示

https://andi.cn/page/621504.html

go语言day11 错误 defer(),panic(),recover()

错误: 创建错误 1)fmt包下提供的方法 fmt.Errorf(" 格式化字符串信息 " , 空接口类型对象 ) 2)errors包下提供的方法 errors.New(" 字符串信息 ") 创建自定义错误 需要实现error接口,而error接口…

【JAVA多线程】线程池概论

目录 1.概述 2.ThreadPoolExector 2.1.参数 2.2.新任务提交流程 2.3.拒绝策略 2.4.代码示例 1.概述 线程池的核心: 线程池的实现原理是个标准的生产消费者模型,调用方不停向线程池中写数据,线程池中的线程组不停从队列中取任务。 实现…

“未来已来·智能共融”高峰论坛在京成功举办

在人工智能技术的澎湃浪潮中,其与传统产业的深度融合正逐步成为驱动区域经济增长的新引擎。2024年7月4号,一场以“未来已来智能共融——探索人类智能与人工智能共生共进的新路径”为主题的高峰论坛在北京电子科技职业学院图书馆圆满落幕,为北京经济技术开发区(简称“北京经开区…

时间处理的未来:Java 8全新日期与时间API完全解析

文章目录 一、改进背景二、本地日期时间三、时区日期时间四、格式化 一、改进背景 Java 8针对时间处理进行了全面的改进,重新设计了所有日期时间、日历及时区相关的 API。并把它们都统一放置在 java.time 包和子包下。 Java5的不足之处: 非线程安全&…

JAVA 课设 满汉楼餐厅点餐系统

一、代码详解 1.总体结构展示 2.总体代码 2.1 libs文件 链接:https://pan.baidu.com/s/1nH-I7gIlsqyMpXDDCFRuOA 提取码:3404 2.2 配置的德鲁连接池 #keyvalue driverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/mhl?rewriteBa…

SAP_MM模块-特殊业务场景下的系统实现方案

一、业务背景 目前公司有一种电商业务,卖的是备品配件,是公司先跟供应商采购,然后再销售给客户,系统账就是按照正常业务来流转,公司进行采购订单入库,然后销售订单出库。 不过这种备品配件,实…

Android使用http加载自建服务器静态网页

最终效果如下图,成功加载了电脑端的静态网页内容,这是一个xml文件。 电脑端搭建http服务器 使用“Apache Http Server”,下载地址是:https://httpd.apache.org/download.cgi。具体操作步骤,参考:Apache …

卫星IoT产品发展前景

卫星IoT产品发展前景 一、概述 卫星IoT产品是指利用卫星通信技术实现物联网设备互联互通的解决方案。随着卫星互联网技术的快速发展,卫星IoT产品正逐渐成为解决偏远地区、海洋、航空等场景下物联网连接问题的重要手段。 二、性能特点 广泛覆盖: 卫星…

ssrf结合redis未授权getshell

目录 漏洞介绍 SSRF Redis未授权 利用原理 环境搭建 利用过程 rockylinux cron计划任务反弹shell 写公钥免密登录 ubuntu 写公钥免密登录 漏洞介绍 SSRF SSRF(server side request forgrey)服务端请求伪造,因后端未过滤用户输入&…

SpringBoot实现多数据源切换

1. 概述 仓库地址:https://gitee.com/aopmin/multi-datasource-demo 随着项目规模的扩大和业务需求的复杂化,单一数据源已经不能满足实际开发中的需求。在许多情况下,我们需要同时操作多个数据库,或者需要将不同类型的数据存储在不…

陶建辉当选 GDOS 全球数据库及开源峰会荣誉顾问

近日,第二十三届 GOPS 全球运维大会暨 XOps 技术创新峰会在北京正式召开。本次会议重点议题方向包括开源数据库落地思考、金融数据库自主可控、云原生时代下数据库、数据库智能运维、数据库安全与隐私、开源数据库与治理。大会深入探讨这些方向,促进了数…

Matplotlib 学习

知识点 1.plot():用于绘制线图和 散点图scatter() 函数:plot() 函数可以接受许多可选参数,用于控制图形的外观,例如:颜色: colorblue 控制线条的颜色。线型: linestyle-- 控制线条的样式,例如虚线。标记…

前端vue后端java使用easyexcel框架下载表格xls数据工具类

一 使用alibaba开源的 easyexcel框架&#xff0c;后台只需一个工具类即可实现下载 后端下载实现 依赖 pom.xml <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.2</version></dependen…

昇思25天学习打卡营第12天|FCN图像语义分割

文章目录 昇思MindSpore应用实践基于MindSpore的FCN图像语义分割1、FCN 图像分割简介2、构建 FCN 模型3、数据预处理4、模型训练自定义评价指标 Metrics 5、模型推理结果 Reference 昇思MindSpore应用实践 本系列文章主要用于记录昇思25天学习打卡营的学习心得。 基于MindSpo…

机械键盘有哪些分类

机械键盘是一种比传统的薄膜键盘更耐用、更快捷、更具有手感的键盘。它的键帽和按键是独立的&#xff0c;能够提供更好的反应速度和操作感。机械键盘在现代化生活中得到了广泛的应用。根据其特性和使用场景&#xff0c;机械键盘可以分为以下几类&#xff1a; 1.轴体分类 机械…