Redis是单线程吗?为什么6.0之后引入了多线程?

Redis是单线程吗?为什么6.0之后引入了多线程?

  • Redis 是单线程吗?
  • Redis 单线程模式是怎样的?
  • Redis 采用单线程为什么还这么快?
  • Redis 6.0 之前为什么使用单线程?
  • Redis 6.0 之后为什么引入了多线程?

Redis 是单线程吗?

Redis 单线程指的是「接收客户端请求->解析请求 ->进行数据读写等操作->发送数据给客户端」这个过程是由一个线程(主线程)来完成的,这也是我们常说 Redis 是单线程的原因。

但是,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)不停轮询这个队列,拿出任务就去执行对应的方法即可。

在这里插入图片描述

关闭文件、AOF 刷盘、释放内存这三个任务都有各自的任务队列:

  • BIO_CLOSE_FILE,关闭文件任务队列:当队列有任务后,后台线程会调用 close(fd) ,将文件关闭;
  • BIO_AOF_FSYNC,AOF刷盘任务队列:当 AOF 日志配置成 everysec 选项后,主线程会把 AOF 写日志操作封装成一个任务,也放到队列中。当发现队列有任务后,后台线程会调用 fsync(fd),将 AOF 文件刷盘,
  • BIO_LAZY_FREE,lazy free 任务队列:当队列有任务后,后台线程会 free(obj) 释放对象 / free(dict) 删除数据库所有对象 / free(skiplist) 释放跳表对象;

Redis 单线程模式是怎样的?

Redis 6.0 版本之前的单线模式如下图:

在这里插入图片描述

图中的蓝色部分是一个事件循环,是由主线程负责的,可以看到网络 I/O 和命令处理都是单线程。 Redis 初始化的时候,会做下面这几件事情:

  • 首先,调用 epoll_create() 创建一个 epoll 对象和调用 socket() 创建一个服务端 socket
  • 然后,调用 bind() 绑定端口和调用 listen() 监听该 socket;
  • 然后,将调用 epoll_ctl() 将 listen socket 加入到 epoll,同时注册「连接事件」处理函数。

初始化完后,主线程就进入到一个事件循环函数,主要会做以下事情:

  • 首先,先调用处理发送队列函数,看是发送队列里是否有任务,如果有发送任务,则通过 write 函数将客户端发送缓存区里的数据发送出去,如果这一轮数据没有发送完,就会注册写事件处理函数,等待 epoll_wait 发现可写后再处理 。
  • 接着,调用 epoll_wait 函数等待事件的到来:
    • 如果是连接事件到来,则会调用连接事件处理函数,该函数会做这些事情:调用 accpet 获取已连接的 socket -> 调用 epoll_ctl 将已连接的 socket 加入到 epoll -> 注册「读事件」处理函数;
    • 如果是读事件到来,则会调用读事件处理函数,该函数会做这些事情:调用 read 获取客户端发送的数据 -> 解析命令 -> 处理命令 -> 将客户端对象添加到发送队列 -> 将执行结果写到发送缓存区等待发送;
    • 如果是写事件到来,则会调用写事件处理函数,该函数会做这些事情:通过 write 函数将客户端发送缓存区里的数据发送出去,如果这一轮数据没有发送完,就会继续注册写事件处理函数,等待 epoll_wait 发现可写后再处理 。

以上就是 Redis 单线模式的工作方式,如果你想看源码解析,可以参考这一篇:为什么单线程的 Redis 如何做到每秒数万 QPS ?(opens new window)

Redis 采用单线程为什么还这么快?

官方使用基准测试的结果是,单线程的 Redis 吞吐量可以达到 10W/每秒,如下图所示:

在这里插入图片描述

之所以 Redis 采用单线程(网络 I/O 和执行命令)那么快,有如下几个原因:

  • Redis 的大部分操作都在内存中完成,并且采用了高效的数据结构,因此 Redis 瓶颈可能是机器的内存或者网络带宽,而并非 CPU,既然 CPU 不是瓶颈,那么自然就采用单线程的解决方案了;
  • Redis 采用单线程模型可以避免了多线程之间的竞争,省去了多线程切换带来的时间和性能上的开销,而且也不会导致死锁问题。
  • Redis 采用了 I/O 多路复用机制处理大量的客户端 Socket 请求,IO 多路复用机制是指一个线程处理多个 IO 流,就是我们经常听到的 select/epoll 机制。简单来说,在 Redis 只运行单线程的情况下,该机制允许内核中,同时存在多个监听 Socket 和已连接 Socket。内核会一直监听这些 Socket 上的连接请求或数据请求。一旦有请求到达,就会交给 Redis 线程处理,这就实现了一个 Redis 线程处理多个 IO 流的效果。

Redis 6.0 之前为什么使用单线程?

我们都知道单线程的程序是无法利用服务器的多核 CPU 的,那么早期 Redis 版本的主要工作(网络 I/O 和执行命令)为什么还要使用单线程呢?我们不妨先看一下Redis官方给出的FAQ (opens new window)。

在这里插入图片描述

核心意思是:CPU 并不是制约 Redis 性能表现的瓶颈所在,更多情况下是受到内存大小和网络I/O的限制,所以 Redis 核心网络模型使用单线程并没有什么问题,如果你想要使用服务的多核CPU,可以在一台服务器上启动多个节点或者采用分片集群的方式。

除了上面的官方回答,选择单线程的原因也有下面的考虑。

使用了单线程后,可维护性高,多线程模型虽然在某些方面表现优异,但是它却引入了程序执行顺序的不确定性,带来了并发读写的一系列问题,增加了系统复杂度、同时可能存在线程切换、甚至加锁解锁、死锁造成的性能损耗。

Redis 6.0 之后为什么引入了多线程?

虽然 Redis 的主要工作(网络 I/O 和执行命令)一直是单线程模型,但是在 Redis 6.0 版本之后,也采用了多个 I/O 线程来处理网络请求,这是因为随着网络硬件的性能提升,Redis 的性能瓶颈有时会出现在网络 I/O 的处理上。

所以为了提高网络 I/O 的并行度,Redis 6.0 对于网络 I/O 采用多线程来处理。但是对于命令的执行,Redis 仍然使用单线程来处理,所以大家不要误解 Redis 有多线程同时执行命令。

Redis 官方表示,Redis 6.0 版本引入的多线程 I/O 特性对性能提升至少是一倍以上。

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,表示启用 N-1 个 I/O 多线程(主线程也算一个 I/O 线程)
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 的压力。

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

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

相关文章

如何使用 ArcGIS Pro 制作地震动画

在做某些汇报的时候,除了图文,如果有动画肯定会成为加分项,这里为大家介绍一下如何使用 ArcGIS Pro 制作地震动画,希望能对你有所帮助。 添加时间 在图层属性内,选择时间选项卡,图层时间选择每个要素具有…

GA-CNN-LSTM多输入时序预测|遗传算法-卷积-长短期神经网络|Matlab

目录 一、程序及算法内容介绍: 基本内容: 亮点与优势: 二、实际运行效果: 三、算法介绍: 四、完整程序下载: 一、程序及算法内容介绍: 基本内容: 本代码基于Matlab平台编译&am…

前端开发指导

前端开发指导 本文介绍了配置前端开发环境需要的软件、配置项等,指导如何开始进行UDM部门前端开发的全流程。本文以Windows系统下在Microsoft Virtual Studio Code中开发为基础。 一、综述 目标:零基础或者新员工依照此文档,能够完成开发环境的搭建及熟悉测试环境的搭建。…

Day 29 MySQL的主从复制集群

一:主从复制 1.主从复制概念 什么是主从复制: ​ 主从复制,是用来建立一个和主数据库完全一样的数据库环境,称为从数据库;主数据库一般是准实时的业务数据库 主从复制的作用: ​ 做数据的热备&#xf…

TriCore: Architecture

说明 本文是 英飞凌 架构文档 TriCore TC162P core archiecture Volume 1 of 2 (infineon.com) 的笔记,稍作整理方便查阅,错误之处,还请指正,谢谢 :) 1. Architecture 2. General Purpose & System Register 名词列表&#…

【基础算法总结】二分查找一

二分查找一 1. 二分查找2.在排序数组中查找元素的第一个和最后一个位置3.x 的平方根4.搜索插入位置 点赞👍👍收藏🌟🌟关注💖💖 你的支持是对我最大的鼓励,我们一起努力吧!😃&#x1…

【程序设计和c语言-谭浩强配套】(适合专升本、考研)

一晃大半年没更新了,这一年一直在备考,想着这几天把前段时间学的c语言给大家分享一下,在此做了一个专栏,有需要的小伙伴可私信获取o。 简介:本专栏所有内容皆适合专升本、考研的复习资料,本人手上也有日常…

Android 系统启动流程源码分析

一、Init进程启动 是一个由内核启动的用户级进程。内核自行启动之后,就通过启动一个用户级程序init的方式,完成引导进程。 启动的代码init.c中的main函数执行过程:system\core\init.c中: 主要下面两个重要的过程: 1…

鹦鹉优化算法原理及代码实现

鹦鹉(Pyrrhura Molinae)表现出四种不同的行为特征:觅食、停留、交流和对陌生人的恐惧。这些行为(如图1所示)在现实环境中构成了我们设计PO动机的基础。 觅食:驯化的鹦鹉(Pyrrhura Molinae)的觅食行为令人着迷,因为个体选择在食物丰富的小群体…

支付宝小程序如何去除页面下拉回弹

描述:支付宝小程序页面下拉时会产生回弹,如果页面上有拖拽功能,会有影响 解决方法: 页面xx.config.js中设置:allowsBounceVertical: “NO” 官方文档:https://opensupport.alipay.com/support/FAQ/7110b5d…

VM虚拟机安装调试(步骤如下图)

VM虚拟机安装调试 随着一顿安装操作,还有enter键敲下,出现如下界面。

深入理解线程的两阶段终止模式:确保线程安全退出

序言 在多线程编程中,线程的安全退出是一个重要的问题。在实际应用中,我们经常需要确保线程在退出时能够完成必要的清理工作,同时避免因资源泄漏或状态不一致而导致的问题。线程的两阶段终止模式是一种解决这个问题的有效方法。本文将深入探…

python之并发编程

python之并发编程 线程的创建方式线程的创建方式(方法包装)线程的创建方式(类包装)join()【让主线程等待子线程结束】守护线程【主线程结束,子线程就结束】 锁多线程操作同一个对象(未使用线程同步)多线程操作同一个对象(增加互斥锁,使用线程同步)死锁案…

[muduo网络库]——muduo库三大核心组件之 Poller/EpollPoller类(剖析muduo网络库核心部分、设计思想)

接着上文,[muduo网络库]——muduo库三大核心组件之Channel类(剖析muduo网络库核心部分、设计思想),本章我们来学习muduo网络库中第二大核心组件Poller/EpollPoller类。 先回顾一下三大核心组件之间的关系。 接着我们进入正题。 P…

Java入门——类和对象(上)

经读者反映与笔者考虑,近期以及往后内容更新将主要以java为主,望读者周知、见谅。 类与对象是什么? C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。 JAVA是基于面向对…

java中的并发编程

1、上下文切换 即使是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现 这个机制。这个时间片特别短,一般是几十毫秒,所以会让我们觉得好多任务同时进行。 CPU通过时间片分配算法来循环执行任务,当前任务执…

Java面试——MyBatis

优质博文:IT-BLOG-CN 一、MyBatis 与 JDBC 的区别 【1】JDBC 是 Java 提供操作数据库的 API;MyBatis 是一个持久层 ORM 框架,底层是对 JDBC 的封装。 【2】使用 JDBC 需要连接数据库,注册驱动和数据库信息工作量大,每…

QT 小项目:登录注册账号和忘记密码(下一章实现远程登录)

一、环境搭建 参考上一章环境 二、项目工程目录 三、主要源程序如下: registeraccountwindow.cpp 窗口初始化: void registeraccountWindow::reginit() {//去掉?号this->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButt…

牛客小白月赛93

B交换数字 题目&#xff1a; 思路&#xff1a;我们可以知道&#xff0c;a*b% mod (a%mod) * (b%mod) 代码&#xff1a; void solve(){int n;cin >> n;string a, b;cin >> a >> b;for(int i 0;i < n;i )if(a[i] > b[i])swap(a[i], b[i]);int num1…

LeetCode 209 长度最小的子数组(滑动窗口and暴力)

、 法一&#xff1a;滑动窗口 //使用滑动窗口来解决问题 //滑动窗口的核心点有&#xff1a; /*1.窗口内是什么&#xff1f;2.如何移动窗口的起始位置&#xff1f;3.如何移动窗口的结束位置&#xff1f;4.两个指针&#xff0c;怎么判断哪个指针是终止指针&#xff0c;哪个指针…