当前位置: 首页 > news >正文

Redis核心技术与实战【学习笔记】 - 9.如何避免单线程模型的阻塞

概述

Redis 被广泛应用的原因是因为它支持高性能访问。所以,我们要重视所有可能影响 Redis 性能的因素(如命令操作、系统配置、关键机制、硬件配置等)。

影响 Redis 性能的 5 大方面的潜在因素分别是:

  • Redis 内部的阻塞式操作
  • CPU 核和 NUMA 架构的影响
  • Redis 关键系统配置
  • Redis 内存碎片
  • Redis 缓冲区

1. Redis 实例有哪些阻塞点?

Redis 实例要和许多对象进行交互,这些不同的交互就会涉及不同的操作,我们来看看客户端和 Redis 实例交互的对象,以及交互时会发送的操作。

  • 客户端:网络 IO,键值对增删改查操作,数据库操作
  • 磁盘: 生成 RDB 快照,记录 AOF 日志,AOF 日志重写。
  • 主从节点:主库生成、传输 RDB 文件,从库接收 RDB 文件、清空数据库、加载 RDB 文件;
  • 切片集群实例:向其他实例传输哈希槽信息,数据迁移。
    在这里插入图片描述

1.1 和客户端交互的阻塞点

网络 IO 有时候会比较慢,但是 Redis 使用了 IO 多路复用机制,避免了主线程一直处在等待网络连接或者请求到来的状态,所以网络 IO 不是导致 Redis 阻塞的因素。

键值对的增删改查操作是 Redis 和 客户端交互的主要部分,也是 Redis 主线程执行的主要任务。所以,复杂度高的增删改查操作会阻塞 Redis。

判断操作复杂度有一个最基本的标准,就是看操作的复杂度是否为 O(N).

Redis 中涉及集合的操作复杂度通常为 O(N),所以我们要重视起来。例如集合元素全量查询 HGETALL、SMEMBERS,以及集合的统计聚合操作,例如求交、并和差集。这些操作可以作为 Redis 的第一个阻塞点:集合的全量查询和聚合操作

另外,集合自身的删除操作,同样也有潜在的阻塞风险。这是因为删除不仅仅只是把数据删除,还要释放键值对占用的内存空间。

你可以不要小瞧内存释放的过程。释放内存只是第一步,为了高效管理内存空间,在应用程序释放时,操作系统需要把释放掉的内存块插入一个空间内存块的链表,以便后续进行管理和再分配。这个过程本身需要一定的时间,而且会阻塞当前释放内存的应用程序,所以,如果一下子释放了大量的内存,空闲内存块链表的操作时间就会增加,相应地就会造成 Redis 主线程的阻塞。

什么时候会释放大量内存呢? 其实就是在删除大量键值对数据的时候,最典型的就是删除了包含了大量元素的集合,也称为 bigkey 删除。下图是测试了不同元素数据的集合在进行删除操作时所消耗的时间:
在这里插入图片描述
根据压测,可以得出结论:

  1. 当数据量从10 万增加到 100 万时,4 大集合类型的删除时间的增长幅度从5倍上升到了近 20 倍。
  2. 集合元素越大,删除所花费的时间越长。
  3. 当删除有 100 个元素的集合时,最大删除时间已经达到了 1.9 秒(Hash 类型)。Redis 的响应时间一般在微妙级别,所以,一个操作达到近 2 秒,不可避免地会阻塞主线程。

很显然,Redis 的第二个阻塞点:bigkey 删除操作

删除操作对 Redis 实例性能的负面影响很大,而且在实际业务开发时很容易被忽略,所以移动要重视它

既然频繁你删除键值对都是潜在的阻塞点,那么,Redis 的数据库级别操作中,清空数据库(如 FLUSHDB 和 FLUSHALL 操作)必然也是一个潜在的阻塞风险,因为它设计到删除和释放所有的键值对。所以,Redis的第三个阻塞点:清空数据库

1.2 和磁盘交互时的阻塞点

磁盘 IO 一般都是比较耗时费力的,需要重点关注。不过,Redis 开发者早就认识到磁盘 IO 会带来阻塞,所以就把 Redis 设计为采用子进程的方式生成 RDB 快照文件,以及执行 AOF 日志重写操作。这样一来,这两个操作由子进程负责执行,慢速的磁盘 IO 就不会阻塞主线了。

但是,Redis 直接记录 AOF 日志时,会根据不同的写回策略对数据做落盘保存。一个 AOF 同步写磁盘的操作的耗时大约是 1~2 ms,如果有大量的写操作需要记录在 AOF 日志中,并同步写回的话,就会阻塞主线程了。这就是 Redis 的第四个阻塞点: AOF 日志荣同步写

1.3 主从节点交互时的阻塞点

主从集群中,主库需要生成 RDB 文件,并传输给从库。主库在复制过程中,创建和传输 RDB 文件都是由子进程来完成,不会阻塞主线程。但是对于从库来说,它在接收了 RDB 文件后,需要使用 LPUSHDB 命令清空当前数据库,这就正好是刚刚分析的第三个阻塞点

此外,从库在情况当前数据库后,还需要把 RDB 文件加载到内存,这个过程的快慢和 RDB 文件的大小密切相关, RDB 文件越大,加载过程越慢,所以 加载 RDB 文件就称为了 Redis 的第五个阻塞点

1.4 切片集群实例交互时的阻塞点

当我们部署 Redis 切片集群时,每个 Redis 实例上分配的哈希槽信息需要在不同实例间进行传输,同时,当需要进行负载均衡或者实例增删时,数据会在不同的实例间进行迁移。不过哈希槽信息量不大,而数据迁移是渐进式执行的,所以一般来说,这两类操作对 Redis 主线程的阻塞风险不大。

不过,如果你使用了 Redis Cluster 方案,而且同时正好迁移的是 bigkey 的话,就会造成主线程的阻塞,因为 Redis Cluster 使用了同步迁移。所以,你要知道,当没有 bigkey 时,切片集群的各实例在进行交互时不会阻塞主线程。

1.5 小结

好了,经过上面分析 Redis 的各种关键操作,总结下刚刚找到的阻塞点:

  • 集合全量查询和聚合操作
  • bigkey 删除
  • 清空数据库
  • AOF 日志同步写
  • 从库加载 RDB 文件

如果在主线程执行这些操作,必然会导致主线程的阻塞。为了避免阻塞式操作,Redis 提供了异步线程机制。所谓异步线程机制,就是指 Redis 会启动一些子线程,然后把一些任务交给这些子线程,让它们在后台完成,而不再由主线程来执行这些任务。使用异步线程机制执行操作,可以避免阻塞主线程。

不过,这个时候,问题来了:这五大阻塞式操作都可以被异步执行吗?

2.哪些阻塞点可以异步执行?

如果一个操作能被异步执行,就意味着它不是 Redis 主线程的关键路径上的操作。关键路径上的操作就是,客户端把请求发给 Redis 后,等着 Redis 返回数据结构的操作。

在这里插入图片描述

  • 主线程收到操作 1 后,因为操作 1 并不用给客户端返回具体数据,所以,主线程可以把它交给后台子线程来完成,同时只要给客户端返回一个 “OK” 结果就行。
  • 在子线程执行操作 1 的时候,客户端又向 Redis 实例发送了操作 2,而此时客户端需要使用操作 2 返回的数据结果的,如果操作 2 不返回结果,那么客户端将一直处于等待状态。

在这个例子中,操作 1 就不算关键路径上的操作,所以可以有后台子线程异步执行。而操作 2 需要把结果返回给客户端,它就是关键路径上的操作,所以主线程立即把这个操作执行完。

对于 Redis 来说,读操作是典型的关键路径操作,因为客户端发送了读操作之后,就会等待读取数据的返回。而 Redis 的第一个阻塞点 “集合全量查询和聚合操作” 都涉及到了读操作,所以它是不能进行异步操作了。

删除操作,并不需要给客户端返回具体的数据结果,所以不算关键路径操作。而我们刚才总结的 第二个阻塞点 “bigkey 删除”,和第三个阻塞点 “清空数据库” 都是对数据做删除,并不在关键路径上。因此可以使用后台子线程来异步执行删除操作

最后,我们看下“从库加载 RDB 文件”这个阻塞点。从库想要对客户端提供数据存取服务,就必须把 RDB 文件加载完成,所以,这个操作也属于关键路径上的操作,必须让从库的主线程来执行。

3.异步的子线程机制

Redis 主线程启动后,会使用操作系统提供的 pthread_create 函数创建 3 个子线程,分别由他们负责 AOF 日志写操作、键值对删除以及文件关闭的异步执行。

主线程通过一个链表形式的任务队列和子线程进行交互。当收到键值对删除和清空数据库的操作时,主线程会把这个操作封装成一个任务,放入任务队列中,然后给客户端返回一个完成信息,表明删除已经完成。

但实际上,这个时候删除还没执行,等到后台子线程从任务队列中读取数据,才实际删除键值对,并释放响应的内存空间。因为,我们把这种异步删除也称为惰性删除(lazy free)。此时,删除或清空操作不会阻塞主线程,这就避免了对主线程的性能影响。

和惰性删除类似,当 AOF 日志配置成 everysec 选项后,主线程会把 AOF 写日志操作封装成一个任务,也放到任务队列中。后台子线程读取任务后,开始自行写入 AOF 日志,这样主线程就不用一直等待 AOF 日志写完了。

下图展示了 Redis 中异步子线程执行机制。
在这里插入图片描述
不过,有个地方需要注意下,异步键值对删除和情况数据库操作是 Redis 4.0 后提供的功能,Redis 也提供了新的命令来执行这两个操作。

  • 异步键值对删除:当你的集合类型中有大量元素(如有百万级别或者千万级别元素)需要删除时,建议你使用 ULINK 命令
  • 清空数据库:可以在 FLUSHDB 和 FLUSHALL 命令后机上 AYSNC 选项,这样可以让后台子线程异步地清空数据库
FLUSHDB AYSNC
FLUSHALL AYSNC

4.小结

本章,我们学习了 Redis 实例允许时的 4 大类交互对象:客户端、磁盘、主从库实例、切片集群实例。基于这 4 大类交互对象,我们梳理了会导致 Redis 性能受损的 5 大阻塞点,包括集合全量查询和聚合操作、bigkey 删除、清空数据库、AOF 日志同步写,以及从库加载 RDB 文件。

在这 5 大阻塞点中,bigkey 删除、情况数据库、AOF 日志同步写不属于关键路径操作,可以使用异步子线程机制来完成。 Redis 在运行时会创建三个子线程,主线程会通过一个任务队列和三个子线程进行交互。子线程会根据任务的具体类型,来执行响应的异步操作。

不过 异步删除操作是 Redis 4.0 以后才有的功能,如果你使用的是 4.0 之前的版本,当你遇到 bigkey 删除时,给你个小建议先使用集合类型提供的 SCAN 命令读取数据,然后再次进行删除。因为 SCAN 命令可以每次只读取一部分数据并删除,这样可以避免一次性删除大量 key 给主线程带来阻塞。
例如,对于 Hash 类型的 bigkey 删除,你可以使用 HSCAN 命令,每次从 Hash 集合中获取一部分键值对(如 200 个),再使用 HDEL 删除这些键值对,这样就可以把删除压力分摊到多次操作中,那么,每次删除操作的耗时就不会太长,也就不会阻塞主线程了。

最后,集合全量查询和聚合操作、从库加载 RDB 文件是在关键路径上,无法使用异步操作来完成,对于这两个阻塞点的小建议:

  • 集合全量查询和聚合操作可以使用 SCAN 命令,分批读取数据,再在客户端进行聚合计算
  • 从库加载 RDB 文件把主库的数据量大小控制在 2~4GB 左右,以保证 RDB 文件能以较快的速度加载。

相关文章:

Redis核心技术与实战【学习笔记】 - 9.如何避免单线程模型的阻塞

概述 Redis 被广泛应用的原因是因为它支持高性能访问。所以,我们要重视所有可能影响 Redis 性能的因素(如命令操作、系统配置、关键机制、硬件配置等)。 影响 Redis 性能的 5 大方面的潜在因素分别是: Redis 内部的阻塞式操作C…...

如何在 JavaScript 中使用 map() 迭代数组

简介 从经典的 for 循环到 forEach() 方法,JavaScript 中有各种技术和方法用于遍历数据集。其中最流行的方法之一是 .map() 方法。.map() 通过在父数组的每个项目上调用特定函数来创建一个数组。.map() 是一个非变异方法,它创建一个新数组,而…...

学习JavaEE的日子 Day19 常用类

Day19 1.包装类的使用 理解:8种基本数据类型对应类 出现原因: ​ Java为纯面向对象语言(万物皆对象),8种基本数据类型不能new对象, ​ 就破坏Java为纯面向对应语言的特征,Java又为8种基本数据类型分别 ​ 匹配了对应的…...

25考研政治备考计划

各位小伙伴大家好,今天给大家分享的是25考研政治复习备考计划。 政治没有基础阶段,直接就是强化,强化的内容也就是听课,刷题。 【时间安排】 *7-9月中 徐涛老师或腿姐强化课,推荐刷肖1000 *9月中-10月中 背腿姐的背…...

漏洞01-目录遍历漏洞/敏感信息泄露/URL重定向

目录遍历漏洞/敏感信息泄露/URL重定向 文章目录 目录遍历敏感信息泄露URL重定向 目录遍历 敏感信息泄露 于后台人员的疏忽或者不当的设计,导致不应该被前端用户看到的数据被轻易的访问到。 比如: ---通过访问url下的目录,可以直接列出目录下…...

软件工程知识梳理4-详细设计

详细设计阶段的根本目标是确定应该怎样具体地实现所要求的系统,也就是说.经过这个阶段的设计工作.应该得出对目标系统的精确描述.从而在编码阶段可以把这个描述直接翻译成用某种程序设计语言书写的程序。 详细设计的的目标不仅仅是逻辑上正确地实现每个模块地功能&a…...

Spring Boot3,启动时间缩短 10 倍!

前面松哥写了一篇文章和大家聊了 Spring6 中引入的新玩意 AOT(见Spring Boot3 新玩法,AOT 优化!)。 文章发出来之后,有小伙伴问松哥有没有做性能比较,老实说,这个给落下了,所以今天…...

Picturesocial | 只要 5 分钟,发现容器编排的秘密武器!

在上一篇文章《Picturesocial | 开发实践:如何在 15 分钟内将应用容器化》,我们讨论了容器以及容器化应用程序所需的步骤。在不考虑将 container 部署到哪里的情况下创建 container,就像把家放在漂浮在海中的货运集装箱里一样,听起…...

GEE数据集——Umbra 卫星合成孔径雷达开放数据

Umbra 合成孔径雷达开放数据 Umbra 卫星生成的合成孔径雷达图像分辨率最高(优于 25 厘米/10 英寸)。合成孔径雷达卫星可以在夜间、透过云层、烟雾和雨水捕捉图像。合成孔径雷达具有监测变化的独特能力。开放数据计划(ODP)对全球十个不同地点进行监测。经常更新新图像。ODP …...

一个vue项目中通过iframe嵌套另外一个vue项目,如何让这两个项目进行通信

文章目录 需求分析父传子子传父 需求 一个vue项目中通过iframe嵌套另外一个vue项目,如何让这两个项目之间进行通信 分析 在Vue项目中通过iframe嵌套另外一个Vue项目时,可以通过postMessage方法实现这两个项目之间的通信。postMessage是HTML5新增加的API…...

上班族学习方法系列文章目录

上班族学习方法系列文章目录 文章目录 上班族学习方法系列文章目录前言一、时间管理二、答题实战 前言 上班族如果想提高自己,那么就得掌握有效的学习方法和良好的时间管理。 一、时间管理 上班族有家有业,考证或者提高学历备考时间不充分。需要学会精…...

《Lua程序设计》-- 学习9

迭代器和泛型for 迭代器和闭包 迭代器(iterator)是一种可以让我们遍历一个集合中所有元素的代码结构。在Lua语言中,通常使用函数表示迭代器:每一次调用函数时,函数会返回集合中的“下一个”元素。 一个闭包就是一个…...

GIS应用水平考试一级—2009 年度第二次

全国信息化工程师——GIS应用水平考试 2009 年度第二次全国统一考试一级 试卷说明: 1、本试卷共9页,6个大题,满分150 分,150 分钟完卷。 2、考试方式为闭卷考试。 3、将第一、二、三題的答案用铅笔涂写到(NCIE-GIS)答题卡上。 4、将第四、五、六题的答案填写到主观题答题卡上…...

【计算机视觉】万字长文详解:卷积神经网络

以下部分文字资料整合于网络,本文仅供自己学习用! 一、计算机视觉概述 如果输入层和隐藏层和之前一样都是采用全连接网络,参数过多会导致过拟合问题,其次这么多的参数存储下来对计算机的内存要求也是很高的 解决这一问题&#x…...

Vue3项目封装一个Element-plus Pagination分页

前言:后台系统分页肯定是离不开的,但是ui框架都很多,我们可以定义封装一种格式,所有项目按到这个结构来做. 实例: 第一步:在项目components组件新建一个分页组件,用来进行封装组件. 第二步:根据官方的进行定义,官方提供的这些,需要我们封装成动态模式 第三步:代码改造 <!-…...

node.js(nest.js控制器)学习笔记

nest.js控制器&#xff1a; 控制器负责处理传入请求并向客户端返回响应。 为了创建基本控制器&#xff0c;我们使用类和装饰器。装饰器将类与所需的元数据相关联&#xff0c;并使 Nest 能够创建路由映射&#xff08;将请求绑定到相应的控制器&#xff09;。 1.获取get请求传参…...

Mybatis 源码系列:领略设计模式在 Mybatis 其中的应用

文章目录 一、Builder模式二、工厂模式三、单例模式四、代理模式五、组合模式六、模板方式模式七、适配器模式八、装饰器模式九、迭代器模式 虽然我们都知道有23种设计模式&#xff0c;但是大多停留在概念层面&#xff0c;真实开发中很少遇到&#xff0c;Mybatis源码中使用了大…...

用的到的linux-文件移动-Day2

前言&#xff1a; 在上一节&#xff0c;我们复习了cd大法和创建生成文件和文件夹的方法&#xff0c;介绍了一些“偷懒”&#xff08;高效&#xff09;的小技巧&#xff0c;本节&#xff0c;我们一起来探讨下&#xff0c;我们对文件移动操作时有哪些可以偷懒的小技巧~ 一、复制…...

红队打靶练习:INFOSEC PREP: OSCP

目录 信息收集 1、arp 2、nmap WEB 信息收集 wpscan dirsearch ssh登录 提权 信息收集 1、arp ┌──(root㉿ru)-[~/kali] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:69:c7:bf, IPv4: 192.168.110.128 Starting arp-scan 1.10.0 with 256 ho…...

【linux】文件修改记录

是的&#xff0c;在Linux上&#xff0c;您可以使用’ find 命令检查最近修改的文件。此实用程序可以搜索在指定天数内修改过的文件。你可以这样使用它: 查找主目录中最近24小时(1天)内修改过的文件。 find ~ -type f -mtime -1命令说明: -“~”表示您的主目录。 ’ -type f…...

Vue学习Element-ui

声明&#xff1a;本文来源于黑马程序员PDF讲义 Ajax 我们前端页面中的数据&#xff0c;如下图所示的表格中的学生信息&#xff0c;应该来自于后台&#xff0c;那么我们的后台和前端是 互不影响的2个程序&#xff0c;那么我们前端应该如何从后台获取数据呢&#xff1f;因为是2…...

存内计算技术—解决冯·诺依曼瓶颈的AI算力引擎

文章目录 存内计算技术背景CSDN首个存内计算开发者社区硅基光电子技术存内计算提升AI算力知存科技存算一体芯片技术基于存内计算的语音芯片的实现挑战 参考文献 存内计算技术背景 存内计算技术是一种革新性的计算架构&#xff0c;旨在克服传统冯诺依曼架构的瓶颈&#xff0c;并…...

数据结构--树

一、树的基本术语 结点:树中的一个独立单元 结点的度:结点下分支的个数 树的度:树中所有结点中度的最大值 非终端结点:度不为0的结点 双亲和孩子:结点下的子树称为该结点的孩子.相应地,该结点称为孩子的双亲 兄弟:同一个双亲的孩子之间 祖先:从根到该结点所经分支上的所…...

计算机网络_1.3电路交换、分组交换和报文交换

1.3电路交换、分组交换和报文交换 一、电路交换1、“电路交换”例子引入2、电路交换的三个阶段3、计算机之间的数据传送不适合采用电路交换 二、分组交换1、发送方&#xff08;1&#xff09;报文&#xff08;2&#xff09;分组&#xff08;3&#xff09;首部 2、交换节点3、接收…...

【AI视野·今日NLP 自然语言处理论文速览 第七十七期】Mon, 15 Jan 2024

AI视野今日CS.NLP 自然语言处理论文速览 Mon, 15 Jan 2024 Totally 57 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Machine Translation Models are Zero-Shot Detectors of Translation Direction Authors Michelle Wastl, Ja…...

神经网络的一些常规概念

epoch&#xff1a;是指所有样本数据在神经网络训练一次&#xff08;单次epoch(全部训练样本/batchsize)/iteration1&#xff09;或者&#xff08;1个epochiteration数 batchsize数&#xff09; batch-size&#xff1a;顾名思义就是批次大小&#xff0c;也就是一次训练选取的样…...

【从零开始的rust web开发之路 三】orm框架sea-orm入门使用教程

【从零开始的rust web开发之路 三】orm框架sea-orm入门使用教程 文章目录 前言一、引入依赖二、创建数据库连接简单链接连接选项开启日志调试 三、生成实体安装sea-orm-cli创建数据库表使用sea-orm-cli命令生成实体文件代码 四、增删改查实现新增数据主键查找条件查找查找用户名…...

SQL中limit的用法

在SQL中&#xff0c;LIMIT是一个用于限制返回结果行数的关键词。它可用于在查询结果中指定返回的行数&#xff0c;从而可以用于分页查询或限制结果集大小。 LIMIT关键词有两种常用的语法格式&#xff1a; LIMIT offset, count&#xff1a;该语法用于指定返回结果的起始位置和…...

vue3 [Vue warn]: Unhandled error during execution of scheduler flush

文章目录 前言一、报错截图二、排除问题思路相关问题 Vue3 优雅解决方法异步组件异同之处&#xff1a;好处&#xff1a;在使用异步组件时&#xff0c;有几个注意点&#xff1a; vue3 定义与使用异步组件 总结 前言 Bug 记录。开发环境运行正常&#xff0c;构建后时不时触发下面…...

【vue2源码】阶段一:Vue 初始化

文章目录 一、项目目录1、主目录2、打包入口 二、构造函数Vue的初始化1、创建 Vue 构造函数2、初始化内容分析2.1 initMixin2.2 stateMixin2.3 eventsMixin2.4 lifecycleMixin2.5 renderMixin 一、项目目录 源码版本&#xff1a;2.7.16 1、主目录 src |-- compiler # 包…...