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

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

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

很多小伙伴都知道缓存的好处,从数据库加载数据过慢时,直接上 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 的时候,多一个心眼,避免事故的发生~

相关推荐

面试官:MySQL的自增ID用完了,怎么办?

来自:Java技术驿站既然这块知识点不清楚,那回头就自己动手实践下。首先,创建一个最简单的表,只包含一个自增id,并插入一条数据。create table t0(id i...

SQL 开发必学:深度解析 NULL 值处理的 6 大核心规则与避坑指南

在数据库开发中,NULL值处理是极易引发逻辑错误的技术难点。本文从SQL标准规范出发,系统梳理NULL值的底层逻辑与工程实践要点,帮助开发者建立完整的NULL值处理知识体系。一、三值逻辑...

SQL查找是否"存在",别再用count了

根据某一条件从数据库表中查询『有』与『没有』,只有两种状态,那为什么在写SQL的时候,还要SELECTCOUNT(*)呢?无论是刚入道的程序员新星,还是精湛沙场多年的程序员老白,都是一如既往...

一文带你掌握shell脚本中的if条件语句,轻松搞定工作需求

#shell编程##linux#...

一文搞懂MySQL的左、右、内、外连接

一、前言1、MySQL中的左连接...

性能测试:Mysql中的空值陷阱(mysql中空值怎么表示)

SQL是一种声明式的语言,我们只需要描述想要的结果(WHAT),而不关心数据库如何实现(HOW);虽然SQL比较容易学习,但是仍然有一些容易混淆和出错的概念。今天我们就来说说SQL中的空值陷阱...

MySQL--常用函数(MySQL常用函数汇总)

介绍MySQL函数,是一种控制流程函数,属于数据库用语言。MySQL数据库中提供了很丰富的函数。MySQL函数包括数学函数、字符串函数、日期和时间函数、条件判断函数、系统信息函数、加密函数、格式化函数...

MySQL函数详解:IF()、IFNULL()、NULLIF()、ISNULL()、CASE

2025年3月27日,MySQL作为最流行的关系型数据库管理系统之一,其丰富的函数库为开发者提供了强大的数据处理能力。本文将详细解析MySQL中常用的条件判断函数:IF()、IFNULL()、NULL...

java迭代器iterator(java迭代器iterator增加一条记录)

/***iterator迭代器Collection接口继承了Iterable接口iterable可迭代的在Iterable接口中定义了iterator()方法用于生成迭代器...

说说Redis的数据类型(redis中的数据类型)

一句话总结Redis核心数据类型包括:String:存储文本、数字或二进制数据。List:双向链表,支持队列和栈操作。Hash:字段-值映射,适合存储对象。Set:无序唯一集合,支持交并差运算。...

一网打尽-HashMap面试题(hashmap数据结构面试)

全文4896字。读完五分钟,即可获得HashMap理解全部面经和原理。坚持就是胜利1、实现原理...

本地缓存GuavaCache(一)(本地缓存caffeine)

在并发量、吞吐量越来越大的情况下往往是离不开缓存的,使用缓存能减轻数据库的压力,临时存储数据。根据不同的场景选择不同的缓存,分布式缓存有Redis,Memcached、Tair、EVCache、Aer...

想月薪过万吗?计算机安卓开发之"集合"

集合的总结:/***Collection*List(存取有序,有索引,可以重复)*ArrayList*底层是数组实现的,线程不安全,查找和修改快,增和删比较慢...

Spring Boot 控制 Controller 接口的4种方式,哪种更适合你?

环境:SpringBoot3.4.2...

这些Java基础知识,诸佬们都还记得嘛(学习,复习,面试均可)

方法重载和方法重写的区别方法重写重写体现在继承关系上。在Java中,子类继承父类,子类就会具备父类所以的特征,以及父类的方法和变量比如动物类有“叫”的方法,小狗小猫分别继承了动物类,重写方法时就可以...