Redis性能大挑战:深入剖析缓存抖动现象及有效应对的战术指南

        在实际应用中,你是否遇到过这样的情况,本来Redis运行的好好的,响应也挺正常,但突然就变慢了,响应时间增加了,这不仅会影响用户体验,还会牵连其他系统。

        那如何排查Redis变慢的情况呢?首先有个问题需要确定,就是确定Redis是否真的变慢了。

Redis基线性能

        要判断Redis是否变慢了,一个最直接的方法就是查看Redis的响应时间。

        大部分情况下,Redis的延迟很低,但是在某些情况下,Redis会出现很高的延迟,可能会达到几秒甚至更长,不过持续的时间又不长,这到底是怎么情况呢?如果出现了响应延迟到秒级别就可以确定Redis变慢了。

        首先需要先确定Redis的延迟绝对值,但是在不同运行环境下,Redis的绝对性能是不同的。所以就需要当前环境的基线性能,所谓基线性能,就是一个系统在低压力、无干扰下的基本性能,这个性能只由当前软硬件环境配置决定。

        基线性能可以通过Redis提供的命令来确定,具体为在redis-cli中添加--intrinsic-latency选项,可以用来检测和统计Redis在运行期间内的最大延迟,这就可以作为基线性能。

redis-cli -h localhost --intrinsic-latency 120
Max latency so far: 1 microseconds.
Max latency so far: 29 microseconds.
Max latency so far: 31 microseconds.
Max latency so far: 34 microseconds.
Max latency so far: 53 microseconds.
Max latency so far: 68 microseconds.
Max latency so far: 103 microseconds.
Max latency so far: 106 microseconds.
Max latency so far: 142 microseconds.
Max latency so far: 158 microseconds.
Max latency so far: 164 microseconds.
Max latency so far: 273 microseconds.
Max latency so far: 296 microseconds.
Max latency so far: 673 microseconds.
Max latency so far: 946 microseconds.
Max latency so far: 2138 microseconds.
Max latency so far: 2234 microseconds.
Max latency so far: 16164 microseconds.2383205581 total runs (avg latency: 0.0504 microseconds / 50.35 nanoseconds per run).
Worst run took 321018x longer than the average latency.

        在自己的电脑上运行命令后,会打印120秒内检测到的最大延迟,可以看到这里的最大延迟为16164微妙,16ms左右。一般情况下,检测120s的时长已经够了。

        一般来说,运行时响应时间和基线性能做对比,如果响应时间达到了基线性能的2倍以上,就可以认定Redis变慢了。

Redis变慢的原因

        一旦发现变慢了,接下来就要查找原因解决这个问题了。这个过程要基于Redis本身的工作原理,并结合和它交互的操作系统、存储以及网络等外部系统的关键机制,在借助一些辅助工具来定位问题,并指定行之有效的解决方案。

        Redis变慢的原因有几下几点,如图所示:Redis变慢的原因

        慢查询指令

        慢查询命令,指在Redis中执行速度慢的命令,这会导致Redis延迟增加。Redis提供的命令操作很多,并不是所有命令都慢,这和命令操作的复杂度有关,所以我们必须知道不同命令的复杂度。

        比如,操作的value为String类型时,由于操作的事hash表,这个操作的复杂度是固定的,都是O(1),除非出现了hash碰撞严重。但操作的数据类型为集合时,如果集合中包含大量的元素,那这个操作复杂度是比较高的,会比较耗时。

        Redis官网提供了各个命令的复杂度:Commands | Redisicon-default.png?t=N7T8https://redis.io/commands/        如果有复杂的操作,就要考虑是否需要用简单的命令来替换。如果使用keys命令,查询大量的key,也会导致慢查询,因为需要扫描所有的键值对,所以生产环境要禁用这样的命令。

        删除key操作

        如果Redis中key过期了是会被自动删除的,这也是Redis的回收机制,Redis 的 key可以设置过期时间,默认情况下,每100ms就会扫描一次,删除过期的key,具体如下:

  • 采样ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP个数的key,并将其中过期的key全部删除;
  • 如果超过25%的key过期了,则重复删除过程,直到过期key的比例降至25%以下。

        如果触发了第二条规则,Redis就会一直删除以释放内存空间。注意,删除操作是阻塞的(Redis 4.0提供了异步机制减少阻塞)。所以一旦触发了就会一直删除key,这样一来,就没办法给客户端提供服务了。

        磁盘I/O:AOF

        为了保证数据的可靠性,Redis提供了AOF和RDB两种机制(想具体了解请查看:Redis持久化(AOF、RDB)用到的写时复制到底是什么-CSDN博客)。其中AOF提供了三种写回策略:always、everysec、no,这三种写回策略依赖文件系统来完成,也就是write和fsync。

        write把日志写到内核的缓冲区,就可以返回了,并不需要等待日志实际写回磁盘;而fsync需要把日志记录写回到磁盘才能返回,时间较长。

        当写回策略配置为everysec和always时,Redis需要调用fsync把日志写回磁盘。但是这两种写回策略的具体情况不一样。但不管怎么说,都会涉及到写回磁盘,而且fsync通常比较耗时,如果Redis主线程执行写回,就会造成阻塞。

        另外AOF日志重写时,也容易阻塞主线程,所以Redis使用了子线程来完成该操作。但是AOF重写会对磁盘又大量的IO操作,同时fsync又需要等到数据写到磁盘才能返回,所以,当AOF重写的压力比较大时,就会导致fsync阻塞,虽然fsync由后台子线程完成,但是主线程会监测fsync的执行进度。

        当主线程使用子线程执行了一次fsync,需要再次把接收的数据写回磁盘,如果主线程发现上一次的fsync还没有执行完,那么它就会阻塞。所以,如果后台线程执行fsync频繁阻塞的话,主线程也会阻塞,导致Redis性能变慢。

        内存大页

        内存大页机制也会影响Redis性能。Linux内核从2.6.38开始支持内存大页,支持2MB大小的内存页分配,而常规的也大小是4KB。你可能会说,Redis是内存数据库,内存大页对Redis不是有好处的吗,减少了内存的分配,但是任何事都有两面性,这时一个权衡的过程。

        Redis为了提高可靠性,提供了持久化的机制。这个过程需要额外的线程来执行,所以不会阻塞主线程为客户端提供服务。如果在持久化的过程中,客户端修改了数据,Redis会才用写时复制(Copy On Write)机制,数据一旦修改了不会直接修改内存中的数据,而是复制一份,然后再进行修改。

        如果采用了内存大页,那Redis就需要拷贝该大页。如果关闭了内存大页,那需要拷贝的页数据只有4kb,可见内存大页会复制大量数据。

        所以正常情况下,关闭内存大页就可以了。

        swap操作

        操作系统swap是将内存数据在内存和磁盘来回换入核换出的机制,涉及到磁盘的读写。所以一旦涉及到了swap,其性能都会收到磁盘性能的影响。

        Redis是内存数据库,内存使用量大,如果用Redis保存海量数据,而且没有控制好内存使用量,就可能会触发swap机制,从而影响性能。一旦触发swap机制,Redis需要操作磁盘才能完成,这回极大的降低Redis的性能。

        关于海量数据的处理请参考:面对海量数据Redis如何应对-CSDN博客

        Redis对于swap的排查有现成的命令,具体这里不做详细介绍了。

        内存碎片

        内存碎片很好理解,明明有内存空间,但是申请时就是无法分配需要的内存空间,这对于使用内存的Redis来说无疑影响巨大。

        内存碎片的原因

  1. 内存不是按需分配的,操作系统为了减少内存的分配次数,每次都是按照固定大小进行内存分配的,例如8字节、16字节、32字节等,比如申请了20字节的内存,但是实际上会分配32字节,此时如果在写入5字节数据,就不用再申请内存了,减少了内存申请的次数。
  2. Redis键值对大小不一和删除操作的影响,内存分配器只能按照固定大小分配内存。所以,分配的内存空间一般都会比申请的空间大一些,不会完全一直,这本身就会造成一定碎片,降低内存空存储效率。键值对会被修改或删除,这会导致空间的扩容和释放。一方面,如果修改后的键值对变大或变小了,就需要占用额外的空间或释放不用的空间。另一方面,删除的键值对就不在需要内存空间了,此时会把空间释放出来,变成空闲空间。

        如何判断内存碎片

        Redis是内存数据库,内存利用率的高低直接关系到Redis运行效率的高低。为了让用户能监控到实时的内存使用情况,Redis自身提供了info命令,可以用来查询内存使用的详细信息

info memory
# Memory
used_memory:1143520
used_memory_human:1.09M
used_memory_rss:1114112
used_memory_rss_human:1.06M
used_memory_peak:1144672
used_memory_peak_human:1.09M
used_memory_peak_perc:99.90%
used_memory_overhead:1096754
used_memory_startup:1079248
used_memory_dataset:46766
used_memory_dataset_perc:72.76%
allocator_allocated:1098416
allocator_active:1076224
allocator_resident:1076224
total_system_memory:8589934592
total_system_memory_human:8.00G
used_memory_lua:37888
used_memory_lua_human:37.00K
used_memory_scripts:0
used_memory_scripts_human:0B
number_of_cached_scripts:0
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
allocator_frag_ratio:1.1
allocator_frag_bytes:18446744073709529424
allocator_rss_ratio:1.00
allocator_rss_bytes:0
rss_overhead_ratio:1.04
rss_overhead_bytes:37888
mem_fragmentation_ratio:1.01
mem_fragmentation_bytes:15696
mem_not_counted_for_evict:0
mem_replication_backlog:0
mem_clients_slaves:0
mem_clients_normal:16986
mem_aof_buffer:0
mem_allocator:libc
active_defrag_running:0
lazyfree_pending_objects:0

        mem_fragmentation_ratio指标表示内存碎片化率,该值大于1但小于1.5时,这种情况是正常的,因为内存碎片是无法避免的。如果该值大于1.5,就表名内存碎片化率比较严重了,超过了50%,这时需要采取措施来处理内存碎片化。

        Redis从4.0版本后提供了专门清理内存碎片化的参数,通过该参数来设置内存碎片化的清理的开始和结束时机,以及占用CPU比例,从而减少碎片清理对Redis本身请求处理的性能影响。

config set activedefrag yes

        这个命令只是启动了自动清理功能,但具体什么时候清理,会受下面两个参数的控制。这两个参数分别设置了触发内存清理的一个条件,如果同时满足这两个条件,就开始清理,清理过程中,只要有一个条件不满足,就停止清理。

  • active-defrag-ignore-bytes 100mb:表示内存碎片的字节数达到100MB时,就开始清理。
  • active-defrag-threshold-lower 10:表示内存碎片空间占操作系统分配给Redis的总空间比例达到10%时,开始清理。

总结

        关于Redis变慢的问题排查就介绍到这里,不知道你有没有清晰的思路,欢迎关注并留言讨论。

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

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

相关文章

[问题记录] vue-router中导航守卫默认跳转login失败

问题 做博客后台的时候发现一个问题,在没启动服务的情况下,后台在 router 中并未读取到配置的情况下,应该默认跳转 login 页面。但是页面始终不跳转,并且伴随多个执行错误弹窗。 router.beforeEach(async (to, from, next) >…

基于JavaWeb+BS架构+SpringBoot+Vue协同过滤算法的体育商品推荐系统的设计和实现

基于JavaWebBS架构SpringBootVue协同过滤算法的体育商品推荐系统的设计和实现 文末获取源码Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 文末获取源码 Lun文目录 1 绪 论 1 1.1项目背景 1 1.2研究意义 2 1.3设计目的 2 1.…

抖捧AI实景自动直播,开启2024直播新篇章!

在如今互联网的时代,各个行业的实体商家都面临着新的挑战与机遇,而传统做线下经营的方式,已经逐渐被直播与短视频宣传所超越,但还有不少商家思维存在于传统的宣传方式上,也错失了很多的机会,今天就给大家介…

基于微信小程序的学校图书管理系统开发与实现

学校图书馆里有大量的各种领域的图书,可供学校各个专业的师生来借阅与做科学和论文研究,但过去图书馆的借阅、归还及图书资料的管理完全依靠图书馆工作人员的手工记录与引导师生找寻借阅的书籍,其耗时费力且低效,开发的学校图书管…

爱情视频相册怎么做?2.14情人节表白/活动视频模板PR剪辑素材

美好爱情故事,情人节表白视频相册怎么做?粉色浪漫的PR情人节表白/活动视频模板剪辑素材mogrt下载。 特征:可编辑文字和调整颜色,通过智能对象替换图像,RGB颜色模式,易于自定义,无需插件&#xf…

groovy XmlParser 递归遍历 xml 文件,修改并保存

使用 groovy.util.XmlParser 解析 xml 文件,对文件进行修改(新增标签),然后保存。 是不是 XmlParser 没有提供方法遍历每个节点,难道要自己写? 什么是递归? 不用说,想必都懂得~ …

2023年度总结 - 职业生涯第一个十年

2023年只剩下最后一周,又到了一年一度该做年末总结的时候了。 回想起去年,还有人专门建立了一个关于年度总结文章汇总的仓库。读了很多篇别人写的,给了我很多的触动和感想。这里的每篇文章都是关于某个人这一整年的生活和工作的轨迹啊。即使你…

搜索与图论第一期 DFS(深度优先搜索)

前言 DFS这部分难度不大,大家应该完全掌握!!! 一、DFS的基本内容 内容: 深度优先遍历图的方法是,从图中某顶点v出发: (1)访问顶点v; (2&#…

圣诞老人遇见 GenAI:利用大语言模型、LangChain 和 Elasticsearch 破译手写的圣诞信件

在北极的中心地带,圣诞老人的精灵团队面临着巨大的后勤挑战:如何处理来自世界各地儿童的数百万封信件。 圣诞老人表情坚定,他决定是时候将人工智能纳入圣诞节行动了。 圣诞老人坐在配备了最新人工智能技术的电脑前,开始在 Jupyter…

【LangChain学习之旅】—(4) 模型I/O:输入提示、调用模型、解析输出

【LangChain学习之旅】—(4) 模型I/O:输入提示、调用模型、解析输出 Model I/OLangChain 中提示模板的构建语言模型为什么选择langchain输出解析总结 Reference:LangChain 实战课 Model I/O 我们可以把对模型的使用过程拆解成三块…

计算机体系结构----计分板(scoreboard)算法

计分板算法简介 计分板记录着所有必要的信息,用来控制以下事情: 每条指令何时可以读取操作数并投入运行(对应着RAW冲突的检测)每条指令何时可以写入结果(对应着WAR冲突的检测)在计分板中,WAW冲…

利用numpy+sklearn+matplotlib演示如何创建和训练线性回归模型

通过一个具体的业务场景来演示如何创建和训练线性回归模型。让我们考虑一个房地产市场的例子,其中我们想要根据房屋的大小(平方英尺)来预测其市场价格。 业务场景 假设你的任务是帮助一个房地产公司预测不同大小房屋的市场价格。你有一份包…

nvm管理多版本Node.js

nvm管理多版本Node.js 可能大家都曾苦恼于Node环境问题,某个项目需要升版本,某项目又需要降,甚至还出现npm版本与Node对不上的情况。 通过nvm进行版本管理,即可解决。 卸载Node 通过命令行输入node -v命令查看是否已安装Node&…

【设计模式】创建型模式之单例模式(Golang实现)

定义 一个类只允许创建一个对象或实例,而且自行实例化并向整个系统提供该实例,这个类就是一个单例类,它提供全局访问的方法。这种设计模式叫单例设计模式,简称单例模式。 单例模式的要点: 某个类只能有一个实例必须…

烟火检测AI边缘计算智能分析网关V4在安防项目中的应用及特点

一、行业背景 随着社会和经济的发展,公共安全和私人安全的需求都在不断增长。人们需要更高效、更准确的安防手段来保障生命财产安全,而人工智能技术正好可以提供这种可能性,通过智能监控、人脸识别、行为分析等手段,大大提高了安防…

详细讲解MybatisPlus实现逻辑删除

目录 前言1. 基本知识2. 实战Demo3. 拓展 前言 对于MybatisPlus的相关知识可在我的博客进行搜索 对应的CRUD相关知识也可看我这篇文章:【Java项目】实战CRUD的功能整理(持续更新) 在讲述逻辑删除这个概念时,先引入另外一个概念&…

借助Gitee将typora图片上传CSDN

概述 前面已经发了一个如何借助Github将typora上的图片上传到csdn上,但这有个缺陷:需要科学上网才能加速查看已经上传到github上的图片,否则就会出现已经上传的图片,无法正常查看的问题 如何解决? 那就可以使用Gite…

wpf的资源路径

1、手动命名空间 xmlns:share"clr-namespace:***;assembly**" 2、资源文件 Pack URI 编译到本地程序集内的资源文件的 pack URI 使用以下授权和路径: 授权:application:///。 路径:资源文件的名称,包括其相对于本地…

坑记(HttpInputMessage)

一、背景知识 public interface HttpInputMessage extends HttpMessage Represents an HTTP input message, consisting of headers and a readable body.Typically implemented by an HTTP request on the server-side, or a response on the client-side.Since: 3.0 Author:…

dotJS 2018 - Anders Hejlsberg - TypeScript: JavaScript的静态类型

如何为像JavaScript这样的动态语言定义和实现静态类型系统,为什么要这样做呢? Anders Hejlsberg探讨了TypeScript是如何产生的,它与经典类型系统有何不同,以及即使你不喜欢类型,它也能给你带来好处。 视频地址: http…