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 文件能以较快的速度加载。

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

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

相关文章

测试大佬是怎么看待测试用例设计的

前言 最近干的最多的事情就是设计测试用例、评审测试用例了,于是我不禁又想到了一个经典的问题:如何设计出优秀的测试用例? 可能有些童鞋看到这个问题会有些不以为然,这有什么好想的?干个测试谁还不会设计测试用例&…

41、WEB攻防——通用漏洞XMLXXE无回显DTD实体伪协议代码审计

文章目录 XXE原理&探针&利用XXE读取文件XXE带外测试XXE实体引用XXE挖掘XXE修复 参考资料:CTF XXE XXE原理&探针&利用 XXE用到的重点知识是XML,XML被设计为传输和存储数据,XML文档结构包括XML声明、DTD文档类型定义&#xf…

XVC767AE102 3BHB007209R0102

XVC767AE102 3BHB007209R0102 XVC767AE102 3BHB007209R0102 GPS99808模块在汽车防盗系统中的应用 ... 详细介绍了 leadtek公司最新款oem模块gps9808在新型智能防盗系统中的 ... 定位模块、cpu控制模块、gps通信模块、传感器检测模块、声光报警模块、电源管理模块 ... 功能…

k8s之基础组件说明

前言 K8S,全称 Kubernetes,是一个用于管理容器的开源平台。它可以让用户更加方便地部署、扩展和管理容器化应用程序,并通过自动化的方式实现负载均衡、服务发现和自动弹性伸缩等功能。 具体来说,Kubernetes 可以将应用程序打包成…

sqli-labs-master靶场训练笔记(1-22|新手村)

2024.1.21 level-1 (单引号装饰) 先根据提示建立一个get请求 在尝试使用单个单引号测试,成功发现语句未闭合报错 然后反手一个 order by 得到数据库共3列,-- 后面加字母防止浏览器吃掉 -- 操作(有些会&#xff09…

全链路压测的关键点是什么?

全链路压测是一种重要的性能测试方法,用于评估应用程序或系统在真实生产环境下的性能表现。通过模拟真实用户行为和流量,全链路压测能够全面评估系统在不同负载下的稳定性和性能表现。本文将介绍全链路压测的关键点,以帮助企业更好地理解和应…

IT行业证书大揭秘:哪些证书含金量最高?

文章目录 📖 介绍 📖📒 证书 📒 📖 介绍 📖 在IT行业,有许多证书被认为是含金量高的,可以帮助个人在职业发展中取得重要的竞争优势。以下是一些IT行业中被认为含金量高的证书&#x…

Docker基础知识

1、什么是Docker?Docker解决了什么问题 一个项目中,部署时需要依赖于node.js、Redis、RabbitMQ、MySQL等,这些服务部署时所需要的函数库、依赖项各不相同,甚至会有冲突。给部署带来了极大的困难。 所以引入了Docker Docker为了…

系统架构设计师-21年-下午答案

系统架构设计师-21年-下午答案 更多软考知识请访问 https://ruankao.blog.csdn.net/ 试题一必答,二、三、四、五题中任选两题作答 试题一 (25分) 说明 某公司拟开发一套机器学习应用开发平台,支持用户使用浏览器在线进行基于机器学习的智能应用开发…

ThreadX系列note-前言

什么是 Azure RTOS ThreadX Azure RTOS ThreadX 是 Microsoft 提供的高级工业级实时操作系统 (RTOS)。它是专门为深度嵌入式实时 IoT 应用程序设计的。 Azure RTOS ThreadX 的特性 Azure RTOS ThreadX 提供高级计划、通信、同步、计时器、内存管理和中断管理功能。此外&#…

【SpringBoot】SpringBoot的自动配置原理

📝个人主页:五敷有你 🔥系列专栏:SpringBoot ⛺️稳重求进,晒太阳 自动配置 啥叫自动配置呢?简单说就是springboot根据我们开发者的行为猜测你要做什么事情,然后把你要用的bean都给你准备…

tcpdump 抓包无法落盘

文章目录 问题背景解决办法 问题背景 在嵌入式设备中(Linux系统),为了分析两个网络节点的通讯问题,往往需要用到tcpdump,抓一个.pcap的包在PC端进行分析。博主在实际操作中发现,抓包无法实时落盘。 解决办法 # 下面的命令是写在…

集线器、交换机、路由器工作原理区别

集线器、交换机、路由器这三种设备是相似的,但是他们处理数据的方式有所不同。 1 集线器 集线器的目的是将内部网络上所有网络设备连接在一起。它具有多个端口,可以接受来自网络设备的以太网连接。集线器不过滤任何数据、也不知道该将数据发到什么地方…

友思特应用 | 微观指尖世界:OCT成像应用之3D指纹提取与识别

欢迎访问官网,探索丰富案例: OCT成像系统 | 光学相干断层扫描 | 谱域OCT | 扫频OCT | 广州友思特科技有限公司 关注“友思特机器视觉与光电”公众号、加入行业交流群或直接联系我们,轻松收获更多技术干货 导读 数字化生活已离不开指纹识别认…

Python学习从0到1 day11 Python数据容器.2.元组

世界安静的时候,我只能听到自己的声音 —— 24.2.1 为什么需要元组? 元组同列表一样,都是可以封装多个,不同类型的元素在内,最大的不同点在于: 列表是可以修改的 元组一旦定义完成,就不可修改 所…

测试C#调用OpenCvSharp和IronOcr从摄像头中识别文字

学习了基于OpenCvSharp获取摄像头数据,同时学习了基于IronOcr的文字识别用法,将这两者结合即是从摄像头中识别文字。本文测试C#调用OpenCvSharp和IronOcr从摄像头中识别文字的基本用法、。   新版Winform项目,在Nuget包管理器中添加以下程序…

检测头篇 | 原创自研 | YOLOv8 更换 SEResNeXtBottleneck 头 | 附详细结构图

左图:ResNet 的一个模块。右图:复杂度大致相同的 ResNeXt 模块,基数(cardinality)为32。图中的一层表示为(输入通道数,滤波器大小,输出通道数)。 1. 思路 ResNeXt是微软研究院在2017年发表的成果。它的设计灵感来自于经典的ResNet模型,但ResNeXt有个特别之处:它采用…

记elasticsearch CPU负载100%问题

记elasticsearch CPU负载100%问题 环境:问题表现:初步排查:日志查询hot_thread 深入查询当前elasticsearch正在运行的Task查看Task详情解决问题对导致问题的原因的几个猜测问题复现:导致问题的原因。json导入规则问题json导入规则…

Linux PC 操作系统如果想要达到 Windows 易用的水平还需要多少年

Linux PC 操作系统如果想要达到 Windows 易用的水平还需要多少年? 在开始前我分享下我的经历,刚入行时遇到一个好公司和师父,给了我机会,两年时间从3k薪资涨到18k的, 我师父给了一些Linux 学习方法和资料,…

制造业实施QMS质量管理系统的作用是什么?

QMS质量管理系统是一个关键的组织管理工具,用于确保产品和服务的质量符合预期标准;通过有效地实施万界星空科技QMS,组织可以确保产品和服务的质量符合预期标准,提升客户满意度,增强市场竞争力。 一、QMS系统的特点&…