百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

用了缓存后,性能反而更慢了?(缓存会导致什么问题)

wptr33 2025-07-19 23:04 21 浏览

很多小伙伴都知道缓存的好处,从数据库加载数据过慢时,直接上 Redis 缓存!

的确,Redis 高性能 KV 存储是后端开发提升性能的一大利器,但是有没有想过,如果使用姿势不对,使用 Redis 后,性能反而会更慢呢?

今天就来盘下使用 Redis 性能变慢的几个原因以及一些应对手段。

1、网络和通信导致的延迟

比如我们现在要往 Redis 里面写入多个 key 和值:

keyvaluenameyupigendermalebaseshanghai......

很多同学会采取一条一条塞入的方式来完成这些键值对的写入,如图:

可以看到,下一条数据的写入需要等待上一条的返回,这个等待时间除了命令的处理时间,其实网络通信的时间也占据了很大一部分

就好比我们网购了 5 件衣服,都送到了快递驿站,此时我们是去驿站一件一件拿回家快呢?还是 5 件衣服一起拿快呢?

答案显而易见,肯定是一起拿快,如果一件一件的拿,很多时间都消耗在路上了!

同理,对于上面 Redis 这种场景,我们需要使用 MSET 这样的聚合命令,通过 批量操作 来提升性能。如图,一趟搞定!

我本地写了段脚本来实际测试了一下,对比使用 for 循环插入 2W 条记录,和利用 mset 命令一次性插入 2W 条数据的耗时。

结果,for 循环花了 5472 毫秒,mset 花了126 毫秒,它们之间差了 40 多倍

由此可见,这种聚合命令在某些时候下,提升性能的效果还是十分可观的!

类似的聚合命令还有很多,比如 MGET、MHSET、HMGET 等等。

除此之外,也可以使用 Pipeline 一次性打包多条命令执行,更进阶的还有 lua 脚本,这里就不多展开了。

2、忽略复杂度高的命令

很多同学都默认 Redis 很快,于是用起 Redis 没啥负担,就是几行代码的事情嘛~

其实像一些普通命令,比如 SET 或 LPUSH 这种问题确实不大。

用我的一台小破机器测试,一条 set 命令消耗的时间在 10 毫秒以内。

但是有一些命令却不是,比如 SORT、LREM、SUNION。举个例子,比如有两个大集合,存了很多很多数据,此时你要取它们的交集,想想是不是很耗时?

我在 200W 条数据量的情况下使用 SUNION 命令测试,耗时近 5000 毫秒,跟正常的一条 set 的10 毫秒可是差了 500 倍!

而且需要注意,Redis 执行命令是单线程的!如果你前面执行了一个比较耗时的命令,假设此时并发度很高,那么就会有一堆命令排队等着前面耗时的那条命令,这个时候就会产生阻塞。

想想看,本来 redis 能处理 500 条命令,现在只能处理一条了,这种情况频繁一点,在高峰期对业务的影响就会很大。因此在生产环境中,需要慎重的使用这些命令,仔细评估集合的数据量,如果数据量不大,那么才能使用。

对了,这里需要特别强调一个命令:keys,很多生产环境的问题都是因为这条命令导致的。我对这个命令记忆尤其深刻,因为之前有个同事因为执行了这个命令导致线上服务雪崩了!

这个命令它会扫描 db 所有 key ,如果比较耗时,特别是当前 reids 有很多 key 的情况下,很容易造成服务的崩溃,从而引发雪崩!

做个狠点的测试,插入 1 亿条数据,然后执行下 keys 来看看到底得耗时多久!开始!

10分钟过去了....

20分钟过去了...

???中间没忍住想利用可视化工具打开看看已经插入了多少条,然后它崩了!!

行吧,1 亿数据确实有点多,我放弃,不插入了。

重启了 redis desktop manager 一看,已经插入了2900w条了

于是在 2900w 条数据时,执行 keys 命令,消耗了大概 50 多秒。

在执行 keys 命令的时候,哪怕执行一个普通的 get 命令,也要一直被阻塞。有的时候,单次查询慢,不一定是查询代码的问题。

所以,建议在生产上禁用这类命令,防止一些同学误用产生重大事故!

3、key的集中过期

除了 keys 的问题,之前在生产环境还遇到一个莫名其妙的 Redis 问题。

当时排查的时候,把我头都快搞秃了!

当时遇到的问题就是 key 的集中过期

Redis 淘汰键值对有两种方式:

  1. 惰性淘汰。当命令请求到这个 key 时,看下它过期没,如果过期则清理
  2. 主动淘汰。Redis 每 100 毫秒会随机扫描 20 个 key,删除其中过期的 key,如果过期率超过 25 % ,则会继续这个过程,过期率超过 25 % ,则会继续这个过程,过期率超过 25 % ,则会继续这个过程。没错,会一直重复这个步骤,直到过期率低于 25 % 或者累计耗时超过 25 毫秒才会终止这个步骤。

就是这个主动淘汰机制,使得如果有大批的数据在同一时间到期,那么主要淘汰每次时长都要拉满,这其实就等于主动给 Redis 加压!

好家伙,这种时间的拉长在慢日志里面是查不到的,因为它不是因为命令本身的耗时长,所以当时排查了非常久,让我摸不住头脑的同时,也让我摸不到头发了。

所以在生产上要避免一大堆 key 同时到期,我们在设置过期时间的时候,可以增加一些随机数来打散它们。比如下列代码:

expire(key, time + random(600))

4、bigkey

最后还有一个非常重要的问题点,也是大伙在使用上需要着重关注的点,就是 bigkey 问题。

可以理解为目前的 key 所占的内存比较大,可能是它的值比较多,或者每个值占用的内存比较多。

就好比一个删除(DEL)操作,在我们眼里它不像两个集合交集这么复杂,但是在 bigkey 场景下,它可能就会出现问题!

我们都知道 Redis 占用内存资源,而内存是有限的,因此如果有不需要的内存需要及时释放,所以就会 DEL 某个 key 来释放内存,然后这个 key 又是个 bigkey ,因此释放的内存比较大,这样一来耗时就会比较久,所以一个简单的 DEL 命令都可能会在高峰期造成阻塞。

就好比咱们平日每天扔垃圾,早上出门把昨晚的垃圾一提一扔,轻松!

假设你假期在家蜗居了 7 天,点了 7 天的外卖,然后你这个懒鬼一点都不想出门,垃圾堆着就多了,此时长假结束,你要出门扔垃圾了,请问你能一趟扔完吗?

针对删除的命令,在 Redis 4.0 之后,可以通过 unlink 代替 del,unlink 释放内存是放在后台线程执行的,不会阻塞主线程,6.0 版本开启 lazy-free 后,释放内存都是放在后台线程执行。

不过以上仅仅只是删除的优化,在业务上我们还是需要避免 bigkey 的产生,对于一些已有的 bigkey,要及时做拆分。


以上就是本期分享,希望大家使用 Redis 的时候,多一个心眼,避免事故的发生~

相关推荐

oracle数据导入导出_oracle数据导入导出工具

关于oracle的数据导入导出,这个功能的使用场景,一般是换服务环境,把原先的oracle数据导入到另外一台oracle数据库,或者导出备份使用。只不过oracle的导入导出命令不好记忆,稍稍有点复杂...

继续学习Python中的while true/break语句

上次讲到if语句的用法,大家在微信公众号问了小编很多问题,那么小编在这几种解决一下,1.else和elif是子模块,不能单独使用2.一个if语句中可以包括很多个elif语句,但结尾只能有一个else解...

python continue和break的区别_python中break语句和continue语句的区别

python中循环语句经常会使用continue和break,那么这2者的区别是?continue是跳出本次循环,进行下一次循环;break是跳出整个循环;例如:...

简单学Python——关键字6——break和continue

Python退出循环,有break语句和continue语句两种实现方式。break语句和continue语句的区别:break语句作用是终止循环。continue语句作用是跳出本轮循环,继续下一次循...

2-1,0基础学Python之 break退出循环、 continue继续循环 多重循

用for循环或者while循环时,如果要在循环体内直接退出循环,可以使用break语句。比如计算1至100的整数和,我们用while来实现:sum=0x=1whileTrue...

Python 中 break 和 continue 傻傻分不清

大家好啊,我是大田。今天分享一下break和continue在代码中的执行效果是什么,进一步区分出二者的区别。一、continue例1:当小明3岁时不打印年龄,其余年龄正常循环打印。可以看...

python中的流程控制语句:continue、break 和 return使用方法

Python中,continue、break和return是控制流程的关键语句,用于在循环或函数中提前退出或跳过某些操作。它们的用途和区别如下:1.continue(跳过当前循环的剩余部分,进...

L017:continue和break - 教程文案

continue和break在Python中,continue和break是用于控制循环(如for和while)执行流程的关键字,它们的作用如下:1.continue:跳过当前迭代,...

作为前端开发者,你都经历过怎样的面试?

已经裸辞1个月了,最近开始投简历找工作,遇到各种各样的面试,今天分享一下。其实在职的时候也做过面试官,面试官时,感觉自己问的问题很难区分候选人的能力,最好的办法就是看看候选人的github上的代码仓库...

面试被问 const 是否不可变?这样回答才显功底

作为前端开发者,我在学习ES6特性时,总被const的"善变"搞得一头雾水——为什么用const声明的数组还能push元素?为什么基本类型赋值就会报错?直到翻遍MDN文档、对着内存图反...

2023金九银十必看前端面试题!2w字精品!

导文2023金九银十必看前端面试题!金九银十黄金期来了想要跳槽的小伙伴快来看啊CSS1.请解释CSS的盒模型是什么,并描述其组成部分。答案:CSS的盒模型是用于布局和定位元素的概念。它由内容区域...

前端面试总结_前端面试题整理

记得当时大二的时候,看到实验室的学长学姐忙于各种春招,有些收获了大厂offer,有些还在苦苦面试,其实那时候的心里还蛮忐忑的,不知道自己大三的时候会是什么样的一个水平,所以从19年的寒假放完,大二下学...

由浅入深,66条JavaScript面试知识点(七)

作者:JakeZhang转发链接:https://juejin.im/post/5ef8377f6fb9a07e693a6061目录由浅入深,66条JavaScript面试知识点(一)由浅入深,66...

2024前端面试真题之—VUE篇_前端面试题vue2020及答案

添加图片注释,不超过140字(可选)1.vue的生命周期有哪些及每个生命周期做了什么?beforeCreate是newVue()之后触发的第一个钩子,在当前阶段data、methods、com...

今年最常见的前端面试题,你会做几道?

在面试或招聘前端开发人员时,期望、现实和需求之间总是存在着巨大差距。面试其实是一个交流想法的地方,挑战人们的思考方式,并客观地分析给定的问题。可以通过面试了解人们如何做出决策,了解一个人对技术和解决问...