2、Redis变慢原因排查(下)

感觉Redis变慢了,这些可能的原因你查了没 ?(下)

Redis变慢排查的上一篇【感觉Redis变慢了,这些可能的原因你查了没 ?(上)】,我们是基于Redis命令为入口,比如命令使用不得当,bigkey问题,以及集中过期问题来看现象和如何进行优化处理的,认真读过的同学想必大家对这些现象和处理方式有了比较深的印象。

本期将基于存储层,比如AOF和RDB持久化、内存分配机制、系统层以及一些额外的影响因素,来看看这些情况是如何导致对Redis造成影响的!

先看下篇的大纲:
在这里插入图片描述
持久化

在服务层影响Redis性能的因素中,在存储层就涉及到持久化可能导致的影响,那到底是在什么情况下会发生呢!
在这里插入图片描述

AOF持久化到磁盘
大家可能想过没,在数据持久化方面,还有影响 Redis 性能的因素,这就是AOF 数据持久化。

这里回顾一下AOF机制和三种刷盘策略
在这里插入图片描述
Redis开启AOF后,工作原理如下:

1:客户端发送命令到服务器,在服务器在执行完一个写命令之后,会以Redis协议格式将被执行的写命令追加到服务器状态的 aof_buf 缓冲区的末尾,要是再执行一个写命令,那么会继续追加到aof_buf 缓冲区末尾,这就是追加方式

2:通过 write() 函数,将 aof_buf 缓冲区的数据写入到 AOF 文件

3:在主服务进程死循环的最后,会调用flushAppendOnlyFile函数,该函数会将aof_buf中的数据写入到内核缓冲区,然后判断使用何种策略进行同步

AOF三种刷盘机制如下图:
在这里插入图片描述
通过同种对三种刷盘机制的分析,可以看出如果一般不建议采用always刷盘方式,这个机制会严重拖慢Redis的性能

如果只是将Redis作为缓存,不计较数据丢失的话,可以使用 no方式

大多数人会选择比较折中的方案 everysec同步机制,既保证了数据安全又兼顾了性能,那这种机制就没有任何问题了吗?

方案没有最完美的,everysec同步机制同样存在导致Redis延迟变慢的情况。

AOF耗时的刷盘操作不是已经创建了一个后台线程去处理吗,怎么还会影响Redis主进程呢?

不过这里有个知识点需要注意,就是:

当后台线程(aof_fsync 线程)调用 fsync 函数同步 AOF 文件时,需要等待,直到写入完成。

当磁盘压力太大的时候,会导致 fsync 操作发生阻塞,主线程调用 write 函数时也会被阻塞。fsync 完成后,主线程执行 write 才能成功返回

也就是说压力到了磁盘IO这边,因此磁盘IO压力过大,同样可能导致Redis主进程阻塞,主进程阻塞了,自然处理用户命令变慢了

排查方式:

1:info Persistence,查看aof_delayed_fsync指标,一直在增加,说明主线程频繁出现被阻塞情况

2:系统日志会有提示信息【Asynchronous AOF fsync is taking too long …】

AOF重写
先看AOF重写机制,这个大多数朋友都很清楚了,这里再回顾一下:

• fork 出一条子线程来将文件重写,在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子线程创建新 AOF 文件期间,记录服务器执行的所有写命令。

• 当子线程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新的 AOF 文件保存的数据库状态与现有的数据库状态一致。

• 最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作

看起来是是很正常的,但是刷盘策略和重写机制一起就可能出现以外

127.0.0.1:6379> config get *append*
1) "no-appendfsync-on-rewrite"
2) "no"
3) "appendonly"
4) "yes"
5) "appendfsync"
6) "everysec"

我们看到redis配置可以得出以下结论:

1:Redis实例使用AOF进行持久化,appendfsync策略采用的是everysec刷盘

2:AOF的文件会越来越大,Redis还有一个rewrite策略,实现AOF文件的重写瘦身

3:但是no-appendfsync-on-rewrite的策略是 no,这就会导致在进行rewrite操作时,append fsync会被阻塞

4:而fsync阻塞,会导致Redis主进程也会阻塞

总结起来就是说,AOF重写机制和AOF持久化刷盘一起发生了,冲突了!
在这里插入图片描述
不过我们可以进行配置将 no-appendfsync-on-rewrite 设置为 yes 。这样可以避免AOF rewrite 重写期间,后台子线程不执行刷盘操作,但是在rewrite 期间会有AOF丢失的风险,需要自己权衡好利弊!

不过我看到过一个相对比较折中的方案,分享给大家:

1:给当前Redis实例添加slave节点,当前节点设置为master, 然后master节点关闭AOF,slave节点开启AOF

2:在master 节点设置将 no-appendfsync-on-rewrite 设置为 yes(避免重写时造成和fsync写磁盘的冲突)

3:为了防止AOF文件越来越大,配置在凌晨低峰期定时手动执行bgrewriteaof命令完成每日一次的AOF重写

4:为避免硬盘空间不足或者IO使用率高影响重写功能,添加磁盘空报警和IO使用率报警保障重写的正常进行

技术问题的处理方案有时候没有最完美的,往往是选择合适自己的方案

fork子进程耗时
先来看看fork是什么

fork 是unix和linux这种操作系统的一个api,而不是Redis的api,fork()用于创建一个子进程,不是子线程

有一点我们可以知道的是fork 的目的最终一定是为了不阻塞主进程来提升 Redis 服务的可用性。

而Redis 开启了后台 RDB 和 AOF rewrite 后,在执行时,都需会主进程创建出一个子进程进行数据的持久化,而这个过程会调用操作系统的fork()操作。
在这里插入图片描述
想具体了解如何进行fork的同学可以看我之前的文章分享:redis aof和rdb

Redis中fork 对内存数据的 copy-On-Write (写时复制) 机制最廉价的实现内存镜像

虚拟内存表是在 fork 的瞬间就需要分配,所以这个操作会造成主线程短时间的卡顿(停止所有读写操作),不过卡顿时间跟Redis内存使用量有关。

GB 量级的 Redis 进行 fork 操作的时间在毫秒级 如果这个Redis实例很大,CPU负载再高些,那么 fork 的耗时就会更长,甚至达到秒级,也就会严重影响 Redis 的访问响应时间

这也就是为什么fork()子进程可能导致Redis变慢的原因了

我们可以通过命令去查看延迟大小

//执行 INFO 命令,查看 latest_fork_usec ,时间微秒latest_fork_usec:15699

碎片化过大
什么是内存碎片?

你可以将内存碎片简单地理解为那些不可用的空闲内存

举个例子:操作系统为你分配了 16 字节的连续内存空间,而你存储数据实际只需要使用 12 字节内存空间,那这多余出来的 4 字节内存空间如果后续没办法再被分配存储其他数据的话,就可以被称为内存碎片
在这里插入图片描述
Redis 内存碎片产生比较常见的 2 个原因:

1、存储存储数据的时候向操作系统申请的内存空间可能会大于数据实际需要的存储空间

2、频繁修改 Redis 中的数据

我们可以通过info memory命令查看内存相关的信息,可以计算出内存碎片率

内存碎片率可通过参数计算:mem_fragmentation_ratio (内存碎片率)= used_memory_rss (操作系统实际分配给 Redis 的物理内存空间大小)/ used_memory(内存分配器为了存储数据实际申请使用的内存空间大小)

Redis清理内存碎片的方式有两种:

• Redis 4.0 以前的低版本,只能通过重启实例来解决,不能自动配置回收

• 从 4.0版本以后,提供了一种内存碎片自动回收的方法,可以通过配置动态开启碎片整理

碎片整理
注意开启内存碎片整理,有可能导致 Redis 服务性能下降

Redis 的碎片整理工作是在主线程中执行的,当其进行碎片整理时,操作系统会把多份数据拷贝到新位置以把原有空间释放出来,这会带来时间开销,而这个过程就会阻塞Redis处理请求

为了降低碎片整理带来的性能影响,Redis 为自动内存碎片整理机制提供了多个参数,具体有:

#是否开启碎片整理
activedefrag yes #碎片大小超过 500MB 时才会触发整理
active-defrag-ignore-bytes 500mb #碎片大小占操作系统分配总空间比超过 20% 时触发整理
active-defrag-threshold-lower 20#碎片整理过程占用的CPU比例不低于 15%,保证整理可以正常执行
active-defrag-cycle-min 15 #碎片整理过程占用的CPU比例不高于70%,一旦超过就暂停整理,
#避免大量的内存拷贝等整理过程占用过多的CPU进而影响正常请求
active-defrag-cycle-max 70 #碎片整理过程中,对于 HashListSetZSet 等成员集合类型一次扫描的元素数量
active-defrag-max-scan-fields 500 

在开启碎片自动整理时,一定要优先评估当前 Redis 服务的负载状态,以及应用程序可接受的响应延迟,合理设置碎片整理的参数值和回收时间段【比如放到凌晨程序定时触发】,来尽可能降低碎片整理期间对Redis服务的影响。

操作系统层Swap被使用
先来了解下什么是Swap

操作系统为了缓解内存不足对应用程序的影响,允许把一部分内存中的数据换到磁盘上,以达到应用程序对内存使用的缓冲,这些内存数据被换到磁盘上的区域,等到那些程序要运行时,再从Swap中恢复保存的数据到内存中,这就是 Swap。

在这里插入图片描述
也就是说内存中的数据被交换到了磁盘中,再次访问数据时,就需要从磁盘上读取,而我们知道访问磁盘的速度是比访问内存慢几个等级的。

Redis作为内存数据库,有个常识一定要记住:所有的数据默认都是在内存中,不存在一部分在内存一部分在磁盘中的情况,除非被迫发生了SWAP。

可以通过以下方式来查看 Redis 进程是否使用到了 Swap:

# 获取Redis 的进程 ID
[root@VM-12-10-opencloudos ~]#  redis-cli info | grep process_id
process_id:2600003# 查看 Redis Swap 使用情况
[root@VM-12-10-opencloudos ~]#  cat /proc/260003/smaps | egrep '^(Swap|Size)'Size:               1296 kB
Swap:                  0 kB
SwapPss:               0 kB
Size:                  4 kB
Swap:                  0 kB
SwapPss:               0 kB
Size:                 20 kB
Swap:                  0 kB
SwapPss:               0 kB
...

每一行 Size 表示 Redis 所用的一块内存大小,Size 下面的 Swap 就表示这块 Size 大小的内存,有多少数据已经被换到磁盘上了

如果这两个值相等,说明这块内存的数据都已经完全被换到磁盘上了

如果真的交换到了内存,对于Redis这种性能要求较高的,对这种延迟还是需要谨慎对待!

针对Swap情况可以参考以下解决方案:

• 建议将Redis的预留内存提高,可以多留个20%左右

• 单独不是Redis实例,避免和其他服务进程竞争使用内存

• 整理内存空间,释放出足够的内存供 Redis 使用,然后释放 Redis 的 Swap

总的来说这种内存余量和Swap情况还是要进行监控,毕竟不可能等到出现了问题才去查,那么只能做事后补救处理了

网络带宽被打满
Redis的性能问题,除了前面提到的各种可能影响因素之外,别忘了还有网络IO也可能存在瓶颈,如果网络存在瓶颈,一样会严重影响Redis性能的。

放在后面讲是默认我们认为网络环境是良好的,一般排查问题会从Redis服务去查,不过如果出现带宽过载情况的话,服务器在 TCP 层和网络层就会出现数据包发送延迟、丢包等情况。
在这里插入图片描述
如果因为流量确实大,那么可以考虑进行扩容,不过最好在运维层就Redis的这些指标进行监控,包括网络流量。

其他因素
除了上面主要列出的一些可能因素,这里也有一些其他可能导致影响的原因

Redis服务配置不合理
比如连接数配置啊,内存上限、前面我们讲的AOF持久化和重写的一些配置等等,合理的配置会尽量避免一些问题的出现

使用连接池
应该使用长连接操作 Redis,避免使用短连接模式,频繁的连接创建与销毁,在高QPS访问时网络开销巨大

cpu绑定进程影响
Redis是单线程模型处理处理用户需求,那么处理的吞吐、效率就会极度依赖CPU的处理能力

为了提高服务性能,降低应用程序在多个 CPU 核心之间的上下文切换带来的性能损耗,通常采用的方案是进程绑定 CPU 的方式提高性能

但是Redis的绑核操作过于复杂,对于单机多实例的管理挑战过高,不建议绑定 CPU来处理,这里也不做深入说明,我也没具体深入了解过了

总结
到这里,关于影响Redis性能的因素下篇就分享完了。

相信如果能耐心地看到这里的同学,想必你肯定已经对 Redis 的变慢该如何处理有了很大的收获,同样对Redis如何进行调优也收获很大。

但是通过上下两篇的内容发现 Redis 的性能问题,涉及到的知识点非常广,几乎涵盖了 CPU、内存、网络、甚至磁盘的方方面面。
在这里插入图片描述

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

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

相关文章

【Hive】——CLI客户端(bin/beeline,bin/hive)

1 HiveServer、HiveServer2 2 bin/hive 、bin/beeline 区别 3 bin/hive 客户端 hive-site.xml 配置远程 MateStore 地址 XML <?xml version"1.0" encoding"UTF-8" standalone"no"?> <?xml-stylesheet type"text/xsl" hre…

Ajax跨域请求

最近使用js构造请求时发生了CORS跨域问题&#xff0c;mark一下 ajax跨域&#xff0c;这应该是最全的解决方案了 | Dailc的个人主页Everything about dailchttps://dailc.github.io/2017/03/22/ajaxCrossDomainSolution.htmlAJAX - 廖雪峰的官方网站研究互联网产品和技术&#…

Java - JVM内存模型及GC(垃圾回收)机制

JVM内存模型 JVM堆内存划分&#xff08;JDK1.8以前&#xff09; JVM堆内存划分&#xff08;JDK1.8之后&#xff09; 主要变化在于&#xff1a; java8没有了永久代&#xff08;虚拟内存&#xff09;&#xff0c;替换为了元空间&#xff08;本地内存&#xff09;。常量池&#…

.NET Core 依赖注入 Microsoft.Extensions.DependencyInjection

文章目录 前言什么是依赖注入C# 使用依赖注入框架介绍 Microsoft.Extensions.DependencyInjectionNuget安装简单单例使用打印结果 自动装配举例自动装配测试用例打印结果自动装配执行顺序测试用例有歧义构造函数渐进式构造函数循环依赖 自动装配结论 手动装配手动注入别名注入 …

【AIGC】大语言模型的采样策略--temperature、top-k、top-p等

总结如下&#xff1a; 图片链接 参考 LLM解码-采样策略串讲 LLM大模型解码生成方式总结 LLM探索&#xff1a;GPT类模型的几个常用参数 Top-k, Top-p, Temperature

如何使用玻璃材质制作3D钻石模型

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 当谈到游戏角色的3D模型风格时&#xff0c;有几种不同的风格&#xf…

【Matlab算法】粒子群算法求解二维非线性优化问题(附MATLAB代码)

粒子群算法求解二维非线性优化问题 前言正文步骤分解代码可视化完整代码实现 前言 二维非线性优化问题是指在二维空间中寻找一个点&#xff0c;使得目标函数在该点取得最小&#xff08;或最大) 值&#xff0c;而这个目标函数是一个非线性函数。数学上&#xff0c;这类问题可以…

《Vue.js设计与实现》—Vue3响应系统的原理

一、响应式数据与副作用函数 1. 副作用函数 1-1 指令材料 在JavaScript中&#xff0c;副作用函数是指在执行过程中对外部环境产生可观察的变化或影响的函数。这种函数通常会修改全局变量、修改传入的参数、执行I/O操作&#xff08;如读写文件或发送网络请求&#xff09;、修…

计算目标检测和语义分割的PR

需求描述 实际工作中&#xff0c;相比于mAP项目更加关心的是特定阈值下的precision和recall结果&#xff1b;由于本次的GT中除了目标框之外还存在多边形标注&#xff0c;为此&#xff0c;计算IoU的方式从框与框之间变成了mask之间&#xff1b; 本文的代码适用于MMDetection下的…

gittee使用教学

一、git简介 Git是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效的处理任何大小项目的版本管理。 核心功能&#xff1a; 项目的版本管理 团队协同开发 二、准备工作 1、下载 Git 2、除了选择安装位置以外&#xff0c;其他都无脑安装 3、检查一下安装情况 win…

C语言-每日刷题练习

[蓝桥杯 2013 省 B] 翻硬币 题目背景 小明正在玩一个“翻硬币”的游戏。 题目描述 桌上放着排成一排的若干硬币。我们用 * 表示正面&#xff0c;用 o 表示反面&#xff08;是小写字母&#xff0c;不是零&#xff09;&#xff0c;比如可能情形是 **oo***oooo&#xff0c;如果…

【从零开始学习JVM | 第三篇】类的生命周期(高频面试)

前言&#xff1a; 在Java编程中&#xff0c;类的生命周期是指类从被加载到内存中开始&#xff0c;到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。 在本文中&#xff0c;我们将深入探讨类的生命周期&#xff0c;从类加载到…

Docker Container(容器)——6

目录&#xff1a; 什么是容器&#xff1f;容器生活案例&#xff1f;为什么需要容器&#xff1f;容器的生命周期 容器 OOM容器异常退出容器暂停容器命令清单容器命令详解 docker createdocker rundocker psdocker logsdocker attachdocker execdocker startdocker stopdocker r…

用Sketch for Mac轻松创作无限可能的矢量绘图

在如今的数码时代&#xff0c;矢量绘图软件成为了许多设计师和创意爱好者的必备工具。而在众多的矢量绘图软件中&#xff0c;Sketch for Mac无疑是最受欢迎的一款。它以其简洁易用的界面和强大的功能&#xff0c;让用户能够轻松创作出无限可能的矢量图形。 首先&#xff0c;Sk…

Jmeter入门

一、下载jmeter 官网下载 下载之后解压&#xff0c;在目录/bin下面找到jmeter.bat双击之后即可启动Jmeter。 二、使用 如下左图&#xff0c;选择语言为中文&#xff0c;可以修改测试计划的名称。如下右图&#xff0c;添加线程组 添加线程组 添加http请求 路径传参方式 …

vue3-自定义组件的使用及传值!!!

1.在vue项目中创建一个自定义组件&#xff08;大多数页面中相同的样式&#xff0c;将其封装到组件中&#xff0c;可重复使用&#xff09; 2.将公共组件引入到你想使用的页面中 结果显示如下&#xff1a; 3.为公共组件传值 4.公共组件接收值&#xff0c;显示在组件上 注意事项&a…

wappalyzer基于插件的网站开发技术解析工具

一、wappalyzer 解释&#xff1a;这是一款强大的工具&#xff0c;其主要能提供一种快速、可靠地检测网站所使用技术栈的方法&#xff0c;也就说说&#xff0c;服务器发来的信息都会被它剖析&#xff0c;然后分析出前端的技术栈&#xff0c;有时后端所使用的技术栈如果网页特征…

串口通信(1)-硬件知识

本文讲解串口通信的硬件知识。让读者快速了解硬件知识&#xff0c;为下一步编写代码做基础。 目录 一、概述 二、串口通信分类 2.1信息的传送方向进行分类 2.2同步通信和异步通信 三、串口协议 3.1 RS232 3.1.1 电气特性 3.1.2 连接器的机械特性 3.1.3 连接类型 3.1…

2023全国职业院校技能大赛信息安全管理与评估正式赛(模块三CTF)

全国职业院校技能大赛高等职业教育组信息安全管理与评估 \任务书\ 模块三 网络安全渗透、理论技能与职业素养 极安云科专注技能竞赛&#xff0c;包含网络建设与运维和信息安全管理与评估两大赛项&#xff0c;及各大CTF&#xff0c;基于两大赛项提供全面的系统性培训&#xf…

Pipenv环境配置+Pytest运行

环境配置 使用Pipenv进行虚拟环境管理&#xff0c;Pipfile为依赖模块管理文件。 安装pipenv&#xff1a;brew install pipenv根项目根目录下执行命令创建虚拟环境&#xff1a; pipenv install在Pycharm中指定项目运行的虚拟环境 &#xff1a;File->Settings->Project:-…