redis 为什么会阻塞

news/2024/4/16 16:55:34/文章来源:https://blog.csdn.net/xingjigongsi/article/details/136417436

目录

前言

客户端交换时的阻塞

redis 磁盘交换的阻塞

主从节点交互的阻塞

切片集群交互时的阻塞

异步执行的演变

redis 异步执行如何实现的


前言

大家对redis 比较熟悉吧,只要做项目都会用到redis,提高系统的吞吐。小米商城抢购高峰18k的qps,redis 在其中扮演着非常重要的角色。有时我们操作不当,redis 阻塞了,影响了整个业务。我记得2018年的时候,顺丰就出现了一个事故,有同学在线上对redis的一个操作,直接阻塞了,影响公司的整个业务,后面这位同学被辞退了。如果他对redis比较了解的话,也不会出现这样的事故。

redis 为什么会阻塞,与它的本身设计有一定关系。大家都知道redis 是单线程的,这个并完全正确,只是说我们对数据的读写是在主线程中完成的。但RDB,aof 重写,删除都是在子线程完成的。我们说的阻塞就是阻塞的主线程,redis 为什么会这么设计,抽时间我会详细讲解。这篇文章我主要从客户端,磁盘,主从节点,切片集群实例 多方面取阐述这个问题。

客户端交换时的阻塞

客户端的功能,与redis 服务器建立连接就有对网络IO的影响,对数据库的增删改查操作。redis 采用的是多路复用机制,避免了主线程一直处在等待网络连接或请求到来的状态,所以,网络 IO 不是导致 Redis 阻塞的因素。剩下的也就是增删改查对redis 的影响,这部分功能也是redis 主要的任务,复杂度高的肯定会阻塞redis。

那么怎么判断操作复杂度高不高?就是看复杂度的O(N),这个复杂度也可以看作是空间复杂度。N越大复杂度越高,越容易阻塞。我们可以对照redis 官网的命令,进行查看。一般集合的操作都是O(N) 的复杂度。比如HGETALL,SMEMBERS,以及集合的聚合统计操作,交,并,差集。这些操作特殊是集合的全量查询和聚合操作。

我们看了查询,删除会不会造成redis 阻塞了。当然会了,删除操作的本质是释放键值对占用的内存空间。它不仅释放内存空间,在Redis 释放内存时,操作系统会把释放掉的内存块插入到一个空闲内存块链表,以便后续进行管理和分配。这个过程需要时间并且会阻塞当前释放内存的应用程序。元素数量越大,这个操作消耗的时间越多,越容易阻塞,这就是我们所说的bigkey 删除。

还有就是清空数据库的操作例如( FLUSHDB 和 FLUSHALL 操作)必然也是一个潜在的阻塞风险,因为它涉及到删除和释放所有的键值对。

redis 磁盘交换的阻塞

磁盘的操作一直是系统的瓶颈,一次读盘需要经过寻道,旋转,传输 这么复杂的操作。很多数据库都用了各种技术来避免经常操作磁盘,比如mysql 用了WAL技术,关于这方面技术描述,可以阅读我的普通索引和唯一索引详解。redis 最大的卖点还是还性能,它保证的是操作的高效性,就没有用这么复杂的技术。redis 与磁盘操作的功能都是放在子线程操作,这样就避免了主线程的阻塞。redis 生成的 RDB 快照,aof 日志重写。但是有一个文件比较特殊,aof 日志,会根据不同写回策略做落盘保存。一个同步写磁盘的操作的耗时大约是 1~2ms,如果有大量的写操作需要记录在 AOF 日志中,并同步写回的话,就会阻塞主线程了。大家可以看到redis 磁盘交换的阻塞主要发生在aof 日志同步写。

主从节点交互的阻塞

一般的架构都是一主多从,主节点写,从节点读。主从同步的大概过程是,主节点身材RDB 快照,这个操作上面已经讲过,是通过子线程生成的,不会阻塞。对于从节点来说就是两个步骤,一个是FLUSHDB 清空当前数据库,这个肯定会阻塞,从节点的redis 会回放RDB 的数据,这个操作会阻塞从节点。加载RDB 文件也会阻塞,最终影响的从节点的读。

切片集群交互时的阻塞

使用 Redis Cluster 作为集群方案,当我们增加实例或删除实例时,数据会在不同实例进行迁移。一般会是渐进式的迁移,如果遇到bigkey 会阻塞节点

异步执行的演变

综上所述:我们讲到了几个阻塞点:集合全量查询和聚合操作、bigkey 删除、FLUSHDB 和 FLUSHALL 、AOF 日志同步写,从库加载RDB 文件。这些都是Redis 的性能的瓶颈,这些也一直困扰着redis 的开发人员。随着版本的递进,发生了一些变化。有些已经放在子线程去执行了,就意味着,它并不是 Redis 主线程的关键路径上的操作。

那么什么是主线程的关键路径上的操作:这就是说,客户端把请求发送给 Redis 后,等着 Redis 返回数据结果的操作,比如获取数据,进行接下来的业务。

按照这个定义来说的话:集合全量查询和聚合操作依旧是关键路径上的操作,依旧会阻塞,这个是无法避免的。

bigkey 删除、FLUSHDB 和 FLUSHALL 并不需要给客户端返回具体数据结果,不算关键路径上的操作。它们算不算阻塞点呢,这个其实挺复杂的。这个叫做惰性删除(lazy free),这个功能 Redis 4.0 以后才有的功能,并不是所有的key 都能异步删除。

关于异步删除我需要补充几点,希望大家做个理性的判断:

  1. lazy-free是4.0新增的功能,但是默认是关闭的,需要手动开启。

  1. 手动开启lazy-free时,有4个选项可以控制,分别对应不同场景下,要不要开启异步释放内存机制:

    a) lazyfree-lazy-expire:key在过期删除时尝试异步释放内存

    b) lazyfree-lazy-eviction:内存达到maxmemory并设置了淘汰策略时尝试异步释放内存

    c) lazyfree-lazy-server-del:执行RENAME/MOVE等命令或需要覆盖一个key时,删除旧key尝试异步释放内存

    d) replica-lazy-flush:主从全量同步,从库清空数据库时异步释放内存

  2. 即使开启了lazy-free,如果直接使用DEL命令还是会同步删除key,只有使用UNLINK命令才会可能异步删除key 而 FLUSHDB ASYNC、FLUSHALL AYSNC 才会异步清空库

  3. 这也是最关键的一点,上面提到开启lazy-free的场景,除了replica-lazy-flush之外,其他情况都只是可能去异步释放key的内存,并不是每次必定异步释放内存的。 开启lazy-free后,Redis在释放一个key的内存时,首先会评估代价,如果释放内存的代价很小,那么就直接在主线程中操作了,没必要放到异步线程中执行(不同线程传递数据也会有性能消耗)。 什么情况才会真正异步释放内存?这和key的类型、编码方式、元素数量都有关系(详细可参考源码中的lazyfreeGetFreeEffort函数):

    a) 当Hash/Set底层采用哈希表存储(非ziplist/int编码存储)时,并且元素数量超过64个

    b) 当ZSet底层采用跳表存储(非ziplist编码存储)时,并且元素数量超过64个

    c) 当List链表节点数量超过64个(注意,不是元素数量,而是链表节点的数量,List的实现是在每个节点包含了若干个元素的数据,这些元素采用ziplist存储) 只有以上这些情况,在删除key释放内存时,才会真正放到异步线程中执行,其他情况一律还是在主线程操作。 也就是说String(不管内存占用多大)、List(少量元素)、Set(int编码存储)、Hash/ZSet(ziplist编码存储)这些情况下的key在释放内存时,依旧在主线程中操作。 可见,即使开启了lazy-free,String类型的bigkey,在删除时依旧有阻塞主线程的风险。所以,即便Redis提供了lazy-free,我建议还是尽量不要在Redis中存储bigkey。

个人理解Redis在设计评估释放内存的代价时,不是看key的内存占用有多少,而是关注释放内存时的工作量有多大。从上面分析基本能看出,如果需要释放的内存是连续的,Redis作者认为释放内存的代价比较低,就放在主线程做。如果释放的内存不连续(大量指针类型的数据),这个代价就比较高,所以才会放在异步线程中去执行。

所以我虽然在以后的版本删除有可能是异步删除,还是不要存储bigkey,对bigkey 进行删除。

如果真的要对bigkey 删除呢,我给你个小建议:先使用集合类型提供的 SCAN 命令读取数据,然后再进行删除。因为用 SCAN 命令可以每次只读取一部分数据并进行删除,这样可以避免一次性删除大量 key 给主线程带来的阻塞。

例如,对于 Hash 类型的 bigkey 删除,你可以使用 HSCAN 命令,每次从 Hash 集合中获取一部分键值对(例如 200 个),再使用 HDEL 删除这些键值对,这样就可以把删除压力分摊到多次操作中,那么,每次删除操作的耗时就不会太长,也就不会阻塞主线程了。

AOF 日志呢,如果 AOF 日志配置成 everysec 选项后,也不会去阻塞,异步执行。

从库加载RDB 文件 呢,这个在从库上需要在主线程执行,这个是不能异步的。为了避免阻塞,在这个地方给大家一个建议:从库加载 RDB 文件:把主库的数据量大小控制在 2~4GB 左右,以保证 RDB 文件能以较快的速度加载。

redis 异步执行如何实现的

有些操作需要异步执行,redis 主线程通过一个链表形式的任务队列和子线程进行交互,等到后台子线程从任务队列中读取任务进行操作。

好了就讲到这些了,其他的希望大家补充

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

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

相关文章

Vue | 基于 vue-admin-template 项目的跨域问题解决方法

目录 一、现存问题 二、解决方法 2.1 修改的第一个地方 2.2 修改的第二个地方 2.3 修改的第三个地方 自存 一、现存问题 报错截图如下: 二、解决方法 2.1 修改的第一个地方 在 .env.development 文件中: # base api # VUE_APP_BASE_API /d…

基于Jupyter快速入门Python,Numpy,Scipy,Matplotlib

文章目录 Jupyter 和 Colab 笔记本PythonPython 版本基础数据类型数字Numbers布尔值Booleans字符串Strings 容器列表List字典Dictionaries集合Sets元组Tuples 函数类 Numpy数组Array数组索引Array indexing数据类型DatatypesArray math广播Broadcasting Scipy图像操作MATLAB文件…

智慧城市中的数字孪生:构建城市管理的未来框架

目录 一、引言 二、数字孪生技术概述 三、数字孪生技术在智慧城市中的应用 1、实时监测与预警 2、模拟与优化 3、智能化决策 4、协同与共享 四、数字孪生技术构建城市管理的未来框架的价值 1、提高管理效率 2、优化资源配置 3、提升公共服务水平 4、增强应对突发事…

耐腐蚀特氟龙塑料材质PFA烧杯超纯试剂反应杯

PFA烧杯在实验过程中可作为储酸容器或涉及强酸强碱类实验的反应容器,用于盛放样品、试剂,也可搭配电热板加热、蒸煮、赶酸用。 外壁均有凸起刻度,直筒设计,带翻边,便于夹持和移动,边沿有嘴,便于…

springboot+xjar加密打包部署教程

需求背景 为了跟上时代的步伐,为了更好的生存。开个玩笑,就是心血来潮,使用xjar加密部署jar包,于是就测试一下。 xjar教程 1-maven配置文件修改 首先找到自己ideal配置的maven文件夹,然后找到apache-maven-3.9.3\co…

C# 多线程(3)——线程池

文章目录 1 定义2 线程池使用3 安全取消线程池中任务 1 定义 线程是计算机宝贵的资源,频繁的创建和销毁线程将会大量的占用计算机资源(为每个线程单独分配内存空间,并且多线程下的CPU时间片的切换也会耗费一定的时间)。为了充分利…

Supplementary Influence Maximization Problem in Social Networks

本论文发表于 IEEE TRANSACTIONS ON COMPUTATIONAL SOCIAL SYSTEMS, VOL. 11, NO. 1, FEBRUARY 2024 Abstract 由于在病毒式营销中的重要应用,影响力最大化(IM)已成为一个经过充分研究的问题。它的目的是找到一小部分初始用户,以…

基于Python3的数据结构与算法 - 12 数据结构(列表和栈)

目录 一、引入 二、分类 三、列表 1. C语言中数组的存储方式 2. Python中列表的存储方式 四、栈 1. 栈的应用 -- 括号匹配问题 一、引入 定义:数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成。简单来说&#x…

异常-Exception

文章目录 异常-Exception常见的运行时异常NullPointerException(空指针异常)ArithmeticException(数学运算异常)ArrayIndexOutOfBoundsException(数组下标越界异常)ClassCastException(类型转换…

浏览器修改接口返回数据展示在页面上

前端自己调试,想修改接口返回来的数据,然后展示在页面上 举例 接口返回了数据,想要修改此数据 这时就可以修改数据了,修改完成保存 然后刷新页面就会使用本地保存的数据了

Linux第68步_旧字符设备驱动的一般模板

file_operations结构体中的函数就是我们要实现的具体操作函数。 注意: register_chrdev()和 unregister_chrdev()这两个函数是老版本驱动使用的。现在新字符设备驱动已经不再使用这两个函数,而是使用Linux内核推荐的新字符设备驱动API函数。 1、创建C…

“TXT文本编辑专家:一键查找,多关键字,高效办公新选择“

办公场景中,我们经常需要处理大量的TXT文本文件,从中筛选出包含特定关键字的内容。传统的文本编辑软件往往功能单一,无法满足多关键字、多文件的同时查找需求。现在,一款专为TXT文本编辑设计的办公软件应运而生,它将为…

ArcGIS学习(十一)公服设施服务区划分与评价

ArcGIS学习(十一)公服设施服务区划分与评价 本任务带来的内容是公服设施服务区划分与公服设施服务区评价。本任务包括两个关卡: 公服设施服务区划分公服设施服务区空间价值评价1.公服设施服务区划分 首先,来看看这个案例的场景和基础数据。我们以上海市图书馆为例进行分析…

使用docker安装运行rabbitmq---阿里云服务器

目录 0、阿里云没开端口的得要去安全组规则去添加: 1、下载RabbitMQ镜像: 2、查看镜像是否下载成功,得到docker镜像id: 3、运行RabbitMQ: 4、查看RabbbitMQ容器是否启动成功: 5、启动RabbitMQ中的插件管理 6、访…

微信小程序 ---- 慕尚花坊 用户管理

01. 用户登录-什么是 token 什么是 Token Token 是服务器生成的一串字符串,用作客户端发起请求的一个身份令牌。当第一次登录成功后,服务器生成一个 Token 便将此 Token 返回给客户端,客户端在接收到 Token 以后,会使用某种方式…

.NetCore6.0实现ActionFilter过滤器记录接口请求日志

文章目录 目的实现案例:一.首先我们新建一个WebApi项目二.配置 appsettings.json 文件,配置日志存放路径三.创建 Model 文件夹,创建AppConfig类和ErrorLog类1.在AppConfig类中编写一个GetConfigInfo方法获取配置文件中的值2.在ErrorLog类中&a…

大数据组件之图数据库JanusGraph图文介绍

前言 大数据时代,面对复杂关联数据的存储与检索需求,图数据库以其强大的关联数据处理能力和直观的图形模型展示,已成为大数据处理领域的一项关键技术。JanusGraph作为一款专门为满足超大规模图数据处理而设计的分布式图数据库系统&#xff0…

STM32 NAND FLASH知识点

1.NAND FLASH的简介 NAND FLASH 的概念是由东芝公司在 1989 年率先提出,它内部采用非线性宏单元模式,为固态大容量内存的实现提供了廉价有效的解决方案。 NAND FLASH 存储器具有容量较大,改写速度快等优点,适用于大量数据的存储&…

SpringBoot整合Redis实现分布式锁

SpringBoot整合Redis实现分布式锁 分布式系统为什么要使用分布式锁? 首先,分布式系统是由多个独立节点组成的,这些节点可能运行在不同的物理或虚拟机器上,它们通过网络进行通信和协作。在这样的环境中,多个节点可能同…

Vue事件处理:.passive修饰符与应用场景

.passive修饰符 passive这个修饰符会执行默认方法。你们可能会问,明明默认执行为什么会设置这样一个修饰符。这就要说一下这个修饰符的本意了。 浏览器只有等内核线程执行到事件监听器对应的JavaScript代码时,才能知道内部是否会调用preventDefa…