【Redis】Redis线程模型
目录
- 1. Redis 是单线程的,还是多线程的?
- 2. Redis单线程模式是怎么样的?
- Redis 单线程模式的优势
- Redis 单线程的局限性
- Redis 单线程的优化策略
- 3. Redis采用单线程为什么还这么快
- 4. Redis 6.0 之前为什么使用单线程?
- 5. Redis 6.0 之后为何引入多线程?
1. Redis 是单线程的,还是多线程的?
Redis 的主要
操作是单线程的,这意味着它在主线程上处理所有请求,而不像一些数据库采用多线程模型。这是因为 Redis 的开发者追求的是简化并发操作、避免线程锁机制带来的复杂性和性能开销。通过单线程模型,Redis 避免了常见的线程切换、死锁等问题,大大提升了效率。
Redis 单线程指的是「接收客户端清求->解析请求 ->进行数据读写等操作->发送数据给客户端」这个过
程是由一个线程(主线程)来完成的,这也是我们常说 Redis 是单线程的原因。对于读写命令来说,Redis 一直是单线程模型。不过,在 Redis 4.0 版本之后引入了多线程来执行一些大键值对的异步删除操作, Redis6.0版本之后引入了多线程来处理网络请求(提高网络 I0 读写性能)
但是,Redis 程序并不是仅仅是单线程的,Redis 在启动的时候,是会启动后台线程(BIO)的:
- Redis 在 2.6 版本,会启动2个后台线程,分别处理关闭文件、AOF 刷盘这两个任务;
- Redis 在 4.0 版本之后,新增了一个新的后台线程,用来
异步释放 Redis 内存
,也就是 lazyfree 线程。例如执行 unlink key/flushdb async/flushall async 等命令,会把这些删除操作交给后台线程来执行好处是不会导致 Redis 主线程卡顿。因此,当我们要删除一个大 key 的时候,不要使用 del 命令删除
因为 del是在主线程处理的,这样会导致 Redis 主线程卡顿,因此我们应该使用unlink 命令来异步删除大key
。
之所以 Redis 为「关闭文件、AOF 刷盘、释放内存
」这些任务创建单独的线程来处理,是因为这些任务的操作都是很耗时的,如果把这些任务都放在主线程来处理,那么 Redis 主线程就很容易发生阻塞,这样就无法处理后续的请求了。
后台线程相当于一个消费者,生产者把耗时任务丢到任务队列中,消费者(BIO)不停轮询这个队列,拿出任务就去执行对应的方法即可。
2. Redis单线程模式是怎么样的?
Redis6.0版本之前的单线程模式如下图:(图片来源小林coding)
在 Redis 6.0 版本之前,Redis 的处理模型完全是 单线程 的,所有操作都在一个线程中完成。这种单线程模式虽然看似简单,但由于 Redis 的设计精巧,结合了高效的内存操作和 I/O 多路复用机制,它依然能够在很多场景下表现出色。
Redis 使用 I/O 多路复用机制
来处理并发客户端连接。在这种机制下,一个线程可以同时监听多个文件描述符(即客户端连接)。当某个文件描述符有事件发生(如客户端发送了命令或等待处理的请求到来),Redis 会处理这些事件,而不是为每个连接创建一个独立的线程。
具体的步骤如下:
1. 事件监听:Redis 的主线程通过 I/O 多路复用(如 epoll、select 等系统调用)监听多个客户端连接。当某个连接有新的请求到来时,Redis 会记录这个事件。
2. 事件处理:当有多个客户端的事件需要处理时,Redis 按照队列的顺序处理每个请求。这意味着 Redis 通过单线程的轮询方式来处理每一个客户端请求。
3. 命令执行:Redis 按顺序处理请求,包括读取命令、执行命令、返回结果等所有操作都在主线程中完成。
Redis 单线程模式的优势
- 避免复杂的线程同步问题
Redis 采用单线程设计的一个主要原因是避免了多线程中的复杂同步问题。在多线程环境下,多个线程对共享资源的并发访问需要使用锁机制来保护数据的完整性,但这也会带来性能开销。单线程的 Redis 不需要考虑加锁问题,因为所有的请求都是在同一个线程中按顺序执行,天然避免了竞争条件(race conditions)、死锁和其他并发问题。
- 高效的内存操作
Redis 是一个基于内存的数据库,大部分数据操作都是直接在内存中进行的。内存操作的速度非常快,通常在微秒级别。这使得即使 Redis 是单线程的,它在处理绝大多数简单的读写请求时也能够表现得非常高效。
- I/O 多路复用
Redis 使用 I/O 多路复用(如 epoll)来处理大量并发连接。I/O 多路复用允许一个线程同时监控多个客户端连接的输入输出,而不会阻塞在某个连接上。这意味着,虽然 Redis 是单线程,但它依然可以高效地处理成千上万的并发连接。
非阻塞 I/O:Redis 的网络操作是非阻塞的,这意味着即便某个连接上没有立即准备好数据,Redis 也不会停下来等待,而是继续处理其他连接上的请求。事件驱动模型:Redis 使用事件驱动模型来处理网络 I/O,即当某个连接上有新的事件(如数据可读、可写等)时,Redis 会立即响应并处理这个事件。这样,Redis 不需要为每个连接分配单独的线程,而是通过事件驱动的方式让单线程高效运作。
Redis 单线程的局限性
虽然 Redis 单线程模式在大多数场景下表现良好,但也存在一些局限性,特别是在特定条件下可能成为瓶颈:
- CPU 密集型任务
虽然 Redis 的大部分操作都是非常轻量的,但在某些 CPU 密集型任务中,如处理非常大的集合或执行复杂的 Lua 脚本,单线程的性能可能会不足。当 Redis 需要在主线程中执行耗时较长的计算时,其他客户端的请求可能会因此被阻塞。
- 大数据量的磁盘 I/O
当 Redis 执行持久化操作时(如将数据写入 AOF 文件或 RDB 文件),写入磁盘是一个相对慢的操作。虽然 Redis 通过异步机制和后台线程来优化某些持久化任务(如 AOF 重写和 RDB 文件生成),但在 6.0 之前版本中,网络 I/O 和命令处理仍然完全依赖主线程,因此在高并发场景下,磁盘 I/O 可能成为性能瓶颈。
- 处理大型数据集的阻塞问题
对于像删除大量数据(如一个包含数百万个元素的哈希或列表)或从主线程直接进行内存清理这样的操作,单线程可能会导致 Redis 短暂阻塞,影响其他请求的处理。为此,Redis 提供了 UNLINK 等命令来将大型对象的删除任务交由后台线程异步处理,减少主线程的负载。
Redis 单线程的优化策略
为了解决单线程的性能瓶颈,Redis 在 6.0 之前引入了一些优化策略:
异步删除:通过命令 UNLINK 和 FLUSHALL/FLUSHDB ASYNC,Redis 能够将大数据块的删除操作交由后台线程异步执行,从而避免删除大对象时主线程的阻塞。Lazy-Free 机制:Redis 提供了“惰性删除”机制,允许用户在删除大对象时,分步释放对象占用的内存,而不是一次性完成,从而减小对主线程的影响。后台持久化:对于 RDB 生成和 AOF 重写操作,Redis 通过后台子进程完成。这些操作涉及到磁盘 I/O,通常比较耗时,但通过子进程执行,避免了主线程的阻塞。
总结:
Redis 6.0 版本之前的单线程模式基于 I/O 多路复用和事件驱动模型,能够高效处理大量并发请求。它的优点在于实现简单、避免复杂的并发问题,同时依赖内存操作和非阻塞 I/O,保证了大多数场景下的高性能表现。
然而,在 CPU 密集型任务、大数据集操作和磁盘 I/O 瓶颈等场景下,单线程模式可能会遇到一些性能瓶颈。因此,在 Redis 6.0版本中,引入了多线程来优化网络 I/O 的处理能力,从而进一步提升 Redis 在高并发场景下的性能表现。
3. Redis采用单线程为什么还这么快
官方使用基准测试的结果是,单线程的 Redis 吞吐量可以达到 10W/每秒,如下图所示:
Redis 采用单线程(处理网络 I/O 和执行命令)却依然能够保持极高的性能,主要有以下几个原因:
- 基于内存的操作
Redis 是一个 内存数据库,所有数据操作都直接在内存中完成。相比传统的磁盘数据库,内存读写速度极快,通常在微秒级别。这使得 Redis 能够非常迅速地处理请求,减少了 I/O 等待时间。
- I/O 多路复用机制
Redis 使用 I/O 多路复用(如 epoll、kqueue 或 select)来同时处理多个客户端连接。尽管 Redis 是单线程的,但它能够高效地处理大量并发连接,因为它不需要为每个连接创建单独的线程或进程,而是通过事件驱动的方式监听和处理多个连接上的请求。多路复用允许 Redis 处理大量并发请求的能力远超一般的单线程程序。
- 非阻塞 I/O
Redis 采用 非阻塞 I/O 模式,主线程不会因为某个 I/O 操作(如等待客户端请求数据或写入响应)而被卡住。即使某个客户端数据没有完全到达,Redis 也不会阻塞,而是先处理其他已经准备好的请求,保证主线程的连续执行和高效处理。
- 单线程避免了锁的开销
多线程编程通常需要处理线程之间的同步问题,比如加锁、解锁等操作,这些都会引入性能开销。Redis 的单线程模型完全避免了这些复杂性,因为所有的操作都是按顺序执行的,不需要使用锁来保证数据一致性。因此,Redis 能够避免常见的多线程编程中的资源竞争、锁定和上下文切换带来的性能损失。
- 简单高效的事件驱动模型
Redis 的事件驱动模型设计简洁,主线程通过事件循环不断监听并处理新的客户端连接、请求和响应。这种模型非常轻量化,减少了上下文切换和系统调用的开销,使得 Redis 在处理大量连接时依然保持高效。
- 高效的数据结构
Redis 的数据结构设计非常高效,它在内存中使用了优化的编码方式(如 ziplist、intset 等)来存储数据。这些数据结构专为快速访问和操作而设计,使得 Redis 可以在单线程模式下快速执行各种数据操作。
4. Redis 6.0 之前为什么使用单线程?
虽然说 Redis 是单线程模型,但实际上,Redis 在 4.0 之后的版本中就已经加入了对多线程的支持。不过,Redis 4.0增加的多线程主要是针对一些大键值对的删除操作的命令,使用这些命令就会使用主线程之外的其他线程来“异步处理”,从而减少对主线程的影响。
Redis官方的F&Q
使用单线程的原因:
- 单线程编程容易并且更容易维护;
- Redis的性能瓶颈不在 CPU,主要在内存和网络IO;
- 多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能。
5. Redis 6.0 之后为何引入多线程?
虽然 Redis 的主要工作(网络 I/O和执行命令)一直是单线程模型,但是在 Redis 6.0 版本之后,也采用多个IO线程来处理网络请求,这是因为随着网络硬件的性能提升,Redis 的性能瓶颈有时会出现在网络 I/O 的处理上
。
所以为了提高网络 I/O 的并行度,Redis 6.0 对于网络 I/O 采用多线程来处理。但是对于命令的执行,Redis 仍然使用单线程来处理,所以不要误解 Redis 有多线程同时执行命令。
Redis 6.0 版本支持的 I/O 多线程特性,默认情况下 I/O 多线程只针对发送响应数据(write client socket),并不会以多线程的方式处理读请求(read client socket)。要想开启多线程处理客户端读请求,就需要把 Redis.conf 配置文件中的 io-threads-do-reads 配置项设为 yes。
//读请求也使用io多线程
io-threads-do-reads yes
同时, Redis.conf 配置文件中提供了IO 多线程个数的配置项。
// io-threads ,表示启用 N-1 个 I/0 多线程(主线程也算一个 I/0 线程)
io-threads 4
关于线程数的设置,官方的建议是如果为4核的 CPU,建议线程数设置为2 或 3,如果为8核 CPU 建议线程数设置为 6,线程数一定要小于机器核数,线程数并不是越大越好。
因此, Redis 6.0 版本之后,Redis 在启动的时候,默认情况下会额外创建6个线程(这里的线程数不包括主线程)
。
- Redis-server:Redis的主线程,主要负责执行命令;
- bio_close_file、bio_aof_fsync、bio_lazy_free:三个后台线程,分别异步处理关闭文件任务、AOF刷盘任务、释放内存任务;
- io_ thd_1、io_thd_2、io_thd_3:三个I/O 线程,io-threads 默认是4,所以会启动3(4-1)个I/O多线程,用来分担 Redis 网络I/O 的压力。
相关文章:

【Redis】Redis线程模型
目录 1. Redis 是单线程的,还是多线程的?2. Redis单线程模式是怎么样的?Redis 单线程模式的优势Redis 单线程的局限性Redis 单线程的优化策略 3. Redis采用单线程为什么还这么快4. Redis 6.0 之前为什么使用单线程?5. Redis 6.0 之…...

Electron构建桌面应用程序,服务于项目的自主学习记录(持续更新...
无所畏惧地面对未知,并将其视为成长的机会 大纲官网快速入门1.安装node.js -- 这里推荐用nvm管理2.脚手架创建3.electron 包安装到应用的开发依赖4.创建主进程(main.js)并启动项目1.创建页面2.配置main.js3.启动项目 -- 效果 进阶 -- 基于项目场景功能使用场景一&am…...

linux Load Average 计算
在内核代码 kernel/sched/loadavg.c 中有一个公式: a1 a0 * e a * (1 - e) 此算法是指数加权移动平均法(Exponential Weighted Moving Average,EMWA),是一种特殊的加权移动平均法,它考虑当前和历史的所有数据&#…...

pandas常用数据格式IO性能对比
前言 本文对pandas支持的一些数据格式进行IO(读写)的性能测试,大数据时代以数据为基础,经常会遇到操作大量数据的情景,数据的IO性能尤为重要,本文对常见的数据格式csv、feather、hdf5、jay、parquet、pick…...

【D3.js in Action 3 精译_031】3.5.2 DIY实战:在 Observable 平台实现带数据标签的 D3 条形图并改造单元测试模块
当前内容所在位置(可进入专栏查看其他译好的章节内容) 第一部分 D3.js 基础知识 第一章 D3.js 简介(已完结) 1.1 何为 D3.js?1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践(上)1.3 数据可…...

华为OD机试真题-字符串分割
题目描述: 给定非空字符串s,将该字符串分割成一些子串,使每个子串的ASCII码值的和均为水仙花数。 1、若分割不成功,则返回0。 2、若分割成功且分割结果不唯一,则返回-1。 3、若分割成功且分割结果唯一,则返…...

编程技巧:提高代码健壮性与可维护性的关键方法(以 Shell 为例)
在脚本编写和自动化工作中,良好的编程技巧对于确保代码的健壮性和可维护性至关重要。以下是一些关键的编程技巧,包括模块化设计、单元测试、版本控制、处理边界条件、错误处理、中间值保存和创建 Flag。本文将通过 Shell 脚本示例来阐述这些技巧的应用。 1. 模块化设计 **定…...

【无标题】ReadableStream is not defined
升级 node 版本到 18 及以上即可解决...

【JVM】高级篇
1 GraalVM 1.1 什么是GraalVM GraalVM是Oracle官方推出的一款高性能JDK,使用它享受比OpenJDK或者OracleJDK更好的性能。 GraalVM的官方网址:https://www.graalvm.org/ 官方标语:Build faster, smaller, leaner applications。 更低的CPU…...

nacos1.4源码-服务发现、心跳机制
nacos的服务发现主要采用服务端主动推送客户端定时拉取;心跳机制通过每5s向服务端发送心跳任务来保活,当超过15s服务端未接收到心跳任务时,将该实例设置为非健康状态;当超过30s时,删除该实例。 1.服务发现 nacos主要采…...

C++ 2D平台游戏开发案例
关于2D平台游戏的C开发案例,包括游戏设计、实现细节、图形渲染和音效处理等内容。虽然无法一次性提供3000字,但我会尽量详细描述各个部分,并确保有足够的深度和广度。 2D平台游戏开发案例 一、游戏设计 游戏概述 游戏名称:“冒险…...

【Webpack--019】TreeShaking
🤓😍Sam9029的CSDN博客主页:Sam9029的博客_CSDN博客-前端领域博主 🐱🐉若此文你认为写的不错,不要吝啬你的赞扬,求收藏,求评论,求一个大大的赞!👍* &#x…...

Docker基本操作命令
Docker 是一个开源的应用容器引擎,允许开发者打包应用以及其依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。主要功能是为开发者提供一个简单…...

开源计算器应用的全面测试计划:确保功能性和可靠性
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...

uni.requestPayment 支付成功之后会走 wx.onAppRoute
uni.requestPayment 是用于发起微信支付的统一接口,而 wx.onAppRoute 是用于监听小程序的路由变化。当 uni.requestPayment 支付成功后,如果发生了页面跳转或者其他路由变化,wx.onAppRoute 会被触发。这个行为是正常的,因为支付成…...

统⼀服务入口 - Gateway
网关介绍 问题 在 spring cloud 体系中我们通过 Eureka,Nacos 解决了服务注册,服务发现的问题,使⽤Spring Cloud LoadBalance解决了负载均衡的问题,使⽤ OpenFeign 解决了远程调⽤的问题. 但是当前所有微服务的接⼝都是直接对外暴露的,可以直接通过外部访问.为了保证对外服务的…...

QGraphicsWidget Class
Header:#include < QGraphicsWidget > qmake:QT += widgets Since:Qt 4.4 Inherits:QGraphicsObject and QGraphicsLayoutItem Inherited By:QGraphicsProxyWidget This class was introduced in Qt 4.4. Public Types enum anonymous {Type }Properties autoFi…...

探讨最好用的AI工具:从日常到创新的应用
文章目录 引言常用AI工具1. 语音助手2. 图像识别软件3. 机器翻译工具4. 智能客服系统 创新AI应用1. 自动驾驶汽车2. 虚拟试衣间3. 医疗影像分析4. 个性化推荐系统 个人体验分享1. 通义灵码2. 文心一言3. 智能写作助手4. 智能家居设备5. DALLE6. Whisper7. Codex8. Gym9. ChatGP…...

Python系统教程005(字符串的格式化输出)
知识回顾 1、默认情况下,input函数接收的数据是字符串类型。 2、字符串类型的关键词是str。 3、\n和\t都是转义字符,\n用来换行,\t用来留出一段固定长度的空白。 4、type函数能够用来查看变量的数据类型 5、数据类型的转换,举…...

六款电脑远程控制软件分享,2024最热门软件合集,总有一款适合你!速来看!
想要随时随地控制自己的电脑? 无论你是办公需求,还是要远程协助他人,一款好用的远程控制软件绝对少不了。 2024年最热门的六款远程控制软件已经为你准备好,总有一款适合你,赶快往下看吧! 1. 安企神系统—…...

优质微信群不再难寻!掌握这些技巧就够了!
在当今信息爆炸的时代,微信群已成为人们交流思想、分享知识、建立人脉的重要平台。无论是专业领域的深入探讨,还是兴趣爱好的自由交流,微信群都能为你提供一个即时互动的虚拟空间。然而,面对海量的微信群信息,如何高效…...

python - mysql操作
Python MySQL 操作 1. 背景介绍 常见的Mysql驱动介绍: MySQL-python:也就是MySQLdb。是对C语言操作MySQL数据库的一个简单封装。遵循了Python DB API v2。但是只支持Python2,目前还不支持Python3。mysqlclient:是MySQL-python的…...

基于Springboot+Vue的服装生产管理信息系统设计与实现(含源码数据库)
1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 在这个…...

75.【C语言】文件操作(2)
承接74.【C语言】文件操作(1)文章 目录 5.详细阐释文件的打开和关闭 1.流 2.标准流 3.文件指针 FILE 两层含义 附:FILE的头文件 4.操作文件的步骤 1.fopen函数 编辑 简写的全称查询 输入&输出的含义 2.fclose函数 3.代码示例 补充:绝对路径和相对路径 注意…...

Redis 使用记录
封装调用redis类 import redis from conf.config import RedisConfigclass RedisConfig:redis_json config_data[redis_config]redis_pwd env.get(project_name).get(pwd)host redis_json.get("host")dialog_states_db redis_json.get("dialog_states_db&q…...

IDEA实用小技巧
1. IDEA代码提示忽略大小写 打开设置,点击Editor–>General–>Code Completion ,然后将右侧的Match Case前面的选框去掉勾选。 2. 快速查找接口RestfulToolkitX插件 该插件可以快速查找接口(快捷键为CTRL\) 还会在侧边栏…...

PEI转染试剂对血清的敏感性研究
在细胞生物学和基因工程领域,聚乙烯亚胺(PEI)作为一种常用的转染试剂,广泛应用于基因的递送。然而,PEI转染试剂对血清的敏感性一直是研究的热点问题。转染过程中,血清作为培养基的成分之一,可能…...

手机怎样改网络ip地址?内容详尽实用
随着网络技术的发展,更改手机IP地址已成为一种常见需求。本文将详细介绍如何在不同网络环境下更改手机IP地址,包括移动网络和WiFi网络,以及同时适用于两种网络的方法,内容详尽实用,干货满满。 一、适用于移动网络&…...

使用Pybind11,Python调用C++动态库
最近学习了一下pybind11,使用python来调用C动态库的模式,在某些场景下有用,这里做一个记录。 环境准备 安装python,我这里安装的是3.12版本 下载Pybind11库,这是一个仅包含头文件的轻量级库,使用起来非常…...

提交gitlab
1.gitlab上新建项目 2.git clone url把新项目拉下来 3.git add ./* 把需要提交的文件全部新增 4.git config --global user.email “yetuo.zhuqxsk.local” 身份认证一下 5.git commit -m “asr语音识别-对外服务” 提交 6.git push origin 推送进去 git init git add . git c…...