【Java】缓存常见问题及解决方式

news2025/1/17 6:17:58

文章目录

  • 一、缓存常见问题
  • 二、数据不一致
    • 2.1、一致性问题
    • 2.2、解决方案
  • 三、缓存穿透
    • 3.1、问题
    • 3.2、解决方案
      • 布隆过滤器
      • 使用布隆过滤器解决缓存穿透
  • 四、缓存击穿
    • 4.1、问题
    • 4.2、解决方案
  • 五、缓存雪崩
    • 5.1、问题
    • 5.2、解决方案
  • 六、大key及热点key
    • 6.1、问题
    • 6.2、解决方案
      • 大key
      • 热点key

一、缓存常见问题

由于引入缓存首先需要考虑的就是缓存更新的方式,之前在缓存更新的几种模式中我们介绍过。除了这个问题还有一些常见的问题,整理出一个表格,如下图所示:

缓存问题产生原因解决方案
缓存不一致同步更新失败、异步更新最终一致
缓存穿透恶意攻击空对象缓存、布隆过滤器
缓存击穿热点key失效互斥更新、随机退避
缓存雪崩缓存挂掉快速失败熔断、主从模式、集群模式、差异失效时间
大key存储value很大、集合数据过多、数据未清理拆分key,清理key
热点key预期外的访问量陡增,如突然出现的爆款商品对key进行rehash然后复制到不同集群,使用读写分离架构

二、数据不一致

2.1、一致性问题

数据不一致的问题,可以说只要使用缓存,就要考虑如何面对这个问题。缓存不一致产生的原因一般有两方面:

  • 选择缓存更新模式的不同造成的不一致,例如[缓存更新的几种模式]的Cache Aside不管是先更新db还是先删除或更新cache,在高并发的情况下都有可能造成不一致的情况,只是不同的更新方式造成不一致的概率不一样,尽可能的选择造成不一致概率最小的更新模式。
  • 系统问题导致失败造成的不一致,在这里就是如缓存服务的机器宕机,网络异常造成的更新失败等。

2.2、解决方案

  • 采用强一致性协议,很少使用。

  • 最终一致性,在绝大部分场景中,特别是互联场景下,大多是保证最终一致性。

    • 重试机制mq

      • 更新数据库,若这一步就失败,更新事务失败回滚。
      • 更新缓存失败,将失败的数据写入mq
      • 消费mq得到失败的数据,重新删除缓存
    • 订阅数据库binlog,解耦缓存更新过程。

三、缓存穿透

3.1、问题

产生这个问题的原因可能是外部的恶意攻击,例如,对用户信息进行了缓存,但恶意攻击者使用不存在的用户id频繁请求接口,导致查询缓存不命中,然后穿透 DB 查询依然不命中。这时会有大量请求穿透缓存访问到 DB,增加数据库压力甚至导致系统宕机。

3.2、解决方案

  • 业务上做非法参数的校验,尽量避免非法请求打到缓存。

  • 对不存在的用户,在缓存中保存一个空对象进行标记,防止相同 ID 再次访问 DB。不过有时这个方法并不能很好解决问题,可能导致缓存中存储大量无用数据。

  • 使用 BloomFilter 过滤器,BloomFilter 的特点是存在性检测,如果 BloomFilter 中不存在,那么数据一定不存在;如果 BloomFilter 中存在,实际数据也有可能会不存在。非常适合解决这类的问题。

布隆过滤器

下面简单介绍下布隆过滤器,布隆过滤器内部维护一个bitArray(位数组), 开始所有数据全部置 0 。当一个元素过来时,能过多个哈希函数(hash1,hash2,hash3…)计算不同的在哈希值,并通过哈希值找到对应的bitArray下标处,将里面的值 0 置为 1 。需要说明的是,布隆过滤器有一个误判率的概念,误判率越低,则数组越长,所占空间越大。误判率越高则数组越小,所占的空间越小。
在这里插入图片描述

以上图为例,具体的写入过程(如有3个hash函数):
假设集合里面有3个元素{x, y, z},哈希函数的个数为3。首先将位数组进行初始化,将里面每个位都设置为0。对于集合里面的每一个元素,将元素依次通过3个哈希函数进行映射,每次映射都会产生一个哈希值,这个值对应位数组上面的一个点,然后将位数组对应的位置标记为1。

查询a元素是否存在集合中的时候,同样的方法将a通过哈希映射到位数组上的3个点。如果3个点的其中有一个点不为1,则可以判断该元素一定不存在集合中。反之,如果3个点都为1,则该元素可能存在集合中。

注意:此处不能判断该元素是否一定存在集合中,可能存在一定的误判率。可以从图中可以看到:假设某个元素通过映射对应下标为4,5,6这3个点。虽然这3个点都为1,但是很明显这3个点是不同元素经过哈希得到的位置,因此这种情况说明元素虽然不在集合中,也可能对应的都是1,这是误判率存在的原因。

布隆过滤器能确定一个值一定不存在,但是不能确定一个值一定存在。缓存穿透正好利用"布隆过滤器能确定一个值一定不存在",因此不存在计算误差。

使用布隆过滤器解决缓存穿透

了解布隆过滤器原理后,我们用布隆过滤器解决缓存穿透问题就很简单了,在缓存前加一层布隆过滤器,利用布隆过滤器bitset存储结构存储数据库中所有值,查询缓存前,先查询布隆过滤器,若一定不存在就返回。
方案对比:

方案使用场景使用成本
缓存空对象1. 空数据量不大
2. 数据频繁变化实时性高
1.代码维护简单
2.需要过多的缓存空间 3. 数据不一致
过滤器1.数据量比较大
2. 数据命中不高
3. 数据相对固定实时性低
1.代码维护复杂
2.缓存空间占用少

四、缓存击穿

4.1、问题

缓存击穿,就是某个热点数据失效时,很多请求这一时间都查不到缓存,然后全部请求并发打到了数据库去查询数据构建缓存,造成数据库压力非常大甚至宕机。

4.2、解决方案

解决这个问题有如下办法:

  • 使用互斥锁更新,保证同一个进程中针对同一个数据不会并发请求到 DB,减小 DB 压力。
 public Object getCache(final String key) {
    Object value = redis.get(key);
    //缓存值过期
    if (value == null) {    
        //加mutexKey的互斥锁
        String mutexKey = mutexKey(key);
        if (redis.setnx(mutexKey, 1, time)) {  
            value = db.get(key);
            redis.set(key, value, time);
            redis.delete(mutexKey);
        } else {
            sleep(100); 
            return get(key);  
        }
    }
    return value;
}
  • 不给热点数据设置过期时间,由后台异步更新缓存,或者在热点数据准备要过期前,提前通知后台线程更新缓存以及重新设置过期时间;
方法优点缺点
互斥锁1.简单易用
2.一致性保证
1.存在线程阻塞的风险
2.数据库访问的压力转到分布式锁上来
异步更新1.相比互斥锁方案,降低线程阻塞的时间1.代码更复杂2.逻辑过期时间会占用一定的内存空间

五、缓存雪崩

5.1、问题

缓存雪崩。产生的原因是:

大量请求同时打到DB上,比如大量key同时过期
缓存服务挂掉,这时所有的请求都会穿透到 DB。

5.2、解决方案

使用快速失败的熔断限流策略,减少 DB 瞬间压力;
使用主从模式和集群模式来尽量保证缓存服务的高可用。
针对多个热点 key 同时失效的问题,可以在缓存时使用固定时间加上一个小的随机数,避免大量热点 key 同一时刻失效。

六、大key及热点key

6.1、问题

大key及热点key的定义(不同公司根据实际情况定义不同):

名词解释
大Key通常以Key的大小和Key中成员的数量来综合判定,例如:
1. Key本身的数据量过大:一个String类型的Key,它的值为5 MB。
2. Key中的成员数过多:一个ZSET类型的Key,它的成员数量为10,000个。
3. Key中成员的数据量过大:一个Hash类型的Key,它的成员数量虽然只有1,000个但这些成员的Value(值)总大小为100 MB。
热Key通常以其接收到的Key被请求频率来判定,例如:
1. QPS集中在特定的Key:Redis实例的总QPS(每秒查询率)为10,000,而其中一个Key的每秒访问量达到了7,000。
2. 带宽使用率集中在特定的Key:对一个拥有上千个成员且总大小为1 MB的HASH Key每秒发送大量的HGETALL操作请求。
3. CPU使用时间占比集中在特定的Key:对一个拥有数万个成员的Key(ZSET类型)每秒发送大量的ZRANGE操作请求。

大key及热点key的问题:

类别说明
大Key1. 客户端执行命令的时长变慢。
2. Redis内存达到maxmemory参数定义的上限引发操作阻塞或重要的Key被逐出,甚至引发内存溢出(Out Of Memory)。
3. 集群架构下,某个数据分片的内存使用率远超其他数据分片,无法使数据分片的内存资源达到均衡。
4. 对大Key执行读请求,会使Redis实例的带宽使用率被占满,导致自身服务变慢,同时易波及相关的服务。
5. 对大Key执行删除操作,易造成主库较长时间的阻塞,进而可能引发同步中断或主从切换。
热点Key1. 占用大量的CPU资源,影响其他请求并导致整体性能降低。
2. 集群架构下,产生访问倾斜,即某个数据分片被大量访问,而其他数据分片处于空闲状态,可能引起该数据分片的连接数被耗尽,新的连接建立请求被拒绝等问题。
3. 在抢购或秒杀场景下,可能因商品对应库存Key的请求量过大,超出Redis处理能力造成超卖。
4. 热Key的请求压力数量超出Redis的承受能力易造成缓存击穿,即大量请求将被直接指向后端的存储层,导致存储访问量激增甚至宕机,从而影响其他业务。

6.2、解决方案

大key

  1. 单key存储value很大
    可以把value对象拆成多份,使用multiGet,这样做的意义在于减少操作在一个节点的压力,分散到多个节点。
    使用hash,每个filed存储对象的各属性。

  2. 集合存储了过多的的值
    将这些元素分拆。以hash为例,原先的正常存取流程是

hget(hashKey, field); 
hset(hashKey, field, value);

现在,固定一个桶的数量,比如 1000, 每次存取的时候,先本地进行rehash,确定了该field落在哪个key上。

newHashKey  =  hashKey + ( *hash*(field) % 1000; 
hset (newHashKey, field, value) ;  
hget(newHashKey, field);
  1. 定期删除过期大key

热点key

  1. 在Redis集群架构中对热Key进行复制
    在Redis集群架构中,由于热Key的迁移粒度问题,无法将请求分散至其他数据分片,导致单个数据分片的压力无法下降。此时,可以将对应热Key进行rehash后复制并迁移至其他数据分片,例如将热Key foo复制出3个内容完全一样的Key并名为foo2、foo3、foo4,将这三个Key迁移到其他数据分片来解决单个数据分片的热Key压力。

该方案的缺点在于需要联动修改代码,同时带来了数据一致性的挑战(由原来更新一个Key演变为需要更新多个Key),仅建议该方案用来解决临时棘手的问题。

  1. 使用读写分离架构
    如果热Key的产生来自于读请求,您可以将实例改造成读写分离架构来降低每个数据分片的读请求压力,甚至可以不断地增加从节点。但是读写分离架构在增加业务代码复杂度的同时,也会增加Redis集群架构复杂度。不仅要为多个从节点提供转发层(如Proxy,LVS等)来实现负载均衡,还要考虑从节点数量显著增加后带来故障率增加的问题。Redis集群架构变更会为监控、运维、故障处理带来了更大的挑战。

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

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

相关文章

【Leetcode刷题】字符串匹配

本篇文章为LeetCode 字符串匹配模块的刷题笔记,仅供参考。 目录 Leetcode28.找出字符串中第一个匹配项的下标Leetcode214.最短回文串Leetcode459.重复的子字符串Leetcode686.重复叠加字符串匹配Leetcode1023.驼峰式匹配Leetcode1392.最长快乐前缀Leetcode1668.最大重…

【SpringBoot】一、SpringBoot3改变新特性

前言 本文适合具有springboot的基础的同学。 SpringBoot3改变&新特性 一、前置条件二、自动配置包位置变化1、Springboot2.X2、Springboot3.X 三、jakata api迁移1、Springboot2.X2、Springboot3.X3、SpringBoot3使用druid有问题,因为它引用的是旧的包 四 新特…

App Crawler

Google官方出了一款App遍历工具App Crawler。 文档:应用抓取工具 | Android 开发者 | Android Developers App Crawler工具是Android Jetpack的一部分,它可自动的运行你的App,不需要编写或维护任何代码。 通过App Crawler运行App&…

实训四:索引与视图 - 索引(teachingdb数据库)

索引与数据库完整性 第1关:索引任务描述相关知识索引是什么索引的分类索引的创建和删除查询表中索引 编程要求参考代码 第2关:删除索引-练习任务描述相关知识编程要求测试说明参考代码 第1关:索引 任务描述 本关任务:为 student…

【Leetcode60天带刷】day21二叉树——530.二叉搜索树的最小绝对差 ,501.二叉搜索树中的众数 ,236. 二叉树的最近公共祖先

题目: 530. 二叉搜索树的最小绝对差 给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数,其数值等于两值之差的绝对值。 示例 1: 输入:root [4,2,6,1,3] 输出&#xff1…

chatgpt赋能python:Python与电影评分

Python与电影评分 近年来,越来越多的人选择通过网络来观看电影。然而,在选择一部电影时,看到的只是电影名称和海报。这时就需要借助电影评分来给自己做出更明智的选择。Python作为一门流行的编程语言,它的应用程序提供了许多有用…

图形视图体系结构(Graphics View)

Graphics View框架结构的主要特点 Graphics View框架结构的主要特点如下。 (1)在Graphics View框架结构中,系统可以利用Qt绘图系统的反锯齿、OpenGL工具来改善绘图性能。 (2)Graphics View支持事件传播体系结构&…

利用Charles进行Mock测试

一、Charles介绍 Charles是一款用Java编写的代理软件,电脑或者手机访问网站首先会访问到Charles代理工具上,由代理工具再把访问数据转发到相应的网站上,所以可以很好的通过设置Charles,对接口的请求和响应进行加工处理。 …

【Linux】Linux权限的概念、Linux权限管理、文件类型和访问权限的设置、粘滞位介绍

文章目录 1.Linux权限的概念2.Linux权限管理2.1文件访问者的分类2.2文件类型的访问权限2.3文件权限值的表示方法2.4文件访问权限的相关设置方法 3.目录的权限4.粘滞位 1.Linux权限的概念 在生活中,一件事情是否允许被一个人做,就是叫做权限,权…

【Leetcode60天带刷】day32回溯算法——122.买卖股票的最佳时机II ,55. 跳跃游戏 ,45.跳跃游戏II

​ 题目: 122. 买卖股票的最佳时机 II 给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。 在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一…

MYSQL数据库应用中的17个关键问题

一、单Master 单Master的情况是普遍存在的,对于很多个人站点、初创公司、小型内部系统,考虑到成本、更新频率、系统重要性等问题,系统只依赖一个单例数据库提供服务,基本上已经满足需求。这种场景下我觉得重点应该关注的话题有上图…

图像预处理 Tricks【1】:Contours

系列文章目录 文章目录 系列文章目录前言1. cv2.findContours()1.1. 方法概述1.2. cv2.findContours()1.2.1. 轮廓检索模式1.2.2. 轮廓逼近方法 2. cv2.drawContours()2.1. 方法概述2.2. cv2.drawContours() 3. cv2.contourArea()3.1. 方法概述3.2. cv2.contourArea()3.3. 存在…

java springboot整合MyBatis联合查询

前面文章 java springboot整合MyBatis做数据库查询操作写了springboot整合MyBatis的方法 并演示了基础查询的语法 根据id查 那么 我们这次来演示联合查询 我们staff 表 内容如下 每条数据 对应的都有一个departmentid 这是 department部门表的外键id department表内容如下 如…

Redis 分布式缓存

分布式缓存 单点 Redis 的问题及解决 数据丢失:实现Redis数据持久化并发能力:搭建主从集群,实现读写分离存储能力:搭建分片集群,利用插槽机制实现动态扩容故障恢复能力:利用哨兵机制,实现健康…

Linux系统编程(进程基础知识讲解)

文章目录 前言一、进程的概念二、进程的生命周期三、进程树四、进程的创建五、一个进程可以执行几个程序?六、子进程中调用execve函数总结 前言 本篇文章来讲解Linux中的进程,进程在Linux中是非常重要的一个知识点,掌握好进程是非常重要的。…

postgresql源码学习(56)—— explain是如何快速估算pg表行数的

当我们需要大致知道表行数,但又不需要很精确时,可以采用以下方法 一、 统计信息 pg_class.reltuples 最简便的方法是利用pg_class.reltuples,类似oracle的num_rows postgres# select reltuples::numeric from pg_class where relnamepgbenc…

VUE 2X 表单数据过滤器 ⑨

目录 文章有误请指正,如果觉得对你有用,请点三连一波,蟹蟹支持✨ V u e j s Vuejs Vuejs收集表单数据过滤器 使用 C o o k i e Cookie Cookie 影响总结 文章有误请指正,如果觉得对你有用,请点三连一波,蟹蟹…

【计算机组成原理】RISC-V模型机的有限状态控制器设计

目录 一、RISC-V模型机的目标指令集 二、RISC-V模型机的部件设计 三、运算及传送指令的数据通路设计 四、访存指令的数据通路设计 五、转移类指令的数据通路设计 六、RISC-V模型机控制单元CU的有限状态机设计 一、RISC-V模型机的目标指令集 取指令并译码:根据…

编译原理笔记16:自下而上语法分析(3)构造 DFA、DFA 对下一步分析的指导(有效项目)

目录 由 NFA 用子集法构造 DFA由 LR(0) 项目直接构造识别活前缀的 DFA构造 DFA求拓广文法 GCLOSURE & GO例: 构造 DFA DFA 指导下一步分析有效项目 看了前面的内容,我们已经了解到:分析表和驱动器算法,是 LR 分析器的核心。 …

实训四:索引与视图 - SQL视图(teachingdb数据库)

SQL视图的定义与操纵 第1关:创建视图任务描述相关知识视图的定义创建视图 编程要求测试说明参考代码 第2关:创建视图-练习一任务描述相关知识编程要求测试说明参考代码 第1关:创建视图 任务描述 本关任务:建立计算机系的学生的视…