【Redis】缓存三大问题与缓存一致性问题

news2024/12/23 8:33:02

缓存三大问题

缓存穿透

缓存穿透是指用户查询的数据在缓存和数据库中都不存在,导致每次请求都会直接落到数据库上,增加数据库负载。

解决方案

1)参数校验

一些不合法的参数请求直接抛出异常信息返回给客户端。比如查询的数据库 id 不能小于 0、传入的邮箱格式不对的时候直接返回错误消息给客户端等等。

2)缓存无效 key

如果缓存和数据库都查不到某个 key 的数据就写一个到 Redis 中去并设置过期时间,具体命令如下:SET key value EX 10086这种方式仅能解决请求的 key 变化不频繁的情况

3)布隆过滤器

把所有可能存在的请求的值都存放在布隆过滤器中,当用户请求过来,先判断用户发来的请求的值是否存在于布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户端,存在的话才会走下面的流程。

4)接口限流

根据用户或者 IP 对接口进行限流,对于异常频繁的访问行为,还可以采取黑名单机制,例如将异常 IP 列入黑名单。

缓存击穿

缓存击穿是指热点数据在缓存过期后,大量请求同时查询该数据,导致请求直接落到数据库上,瞬间对数据库造成巨大压力。

解决方案

  1. 针对热点数据提前预热,将其存入缓存中并设置合理的过期时间比如秒杀场景下的数据在秒杀结束之前不过期。

  2. 查询缓存过期后,先获取互斥锁,然后再将数据库中的数据写入到缓存中,保证只有一个请求会落到数据库上,减少数据库的压力。

  3. 缓存中添加一个逻辑过期字段,如果查询到缓存过期,先获取互斥锁,然后开启一个新线程重构缓存过期时间,其他线程如果获取互斥锁失败,直接返回过期数据,从而减少到达数据库的请求。

缓存雪崩

缓存在同一时间大面积的失效(可能是Redis宕机或缓存大面积过期),导致大量的请求都直接落到了数据库上,对数据库造成了巨大的压力。

解决方案

1)针对 Redis 服务不可用的情况:

  1. 集群部署: 采用 Redis 集群,避免单机出现问题整个缓存服务都没办法使用。
  2. 限流与降级:对请求进行限流,防止大量请求同时落到数据库上。同时,在缓存不可用时,可以返回默认值或进行降级处理。
  3. 使用多级缓存架构:结合本地缓存和分布式缓存,减少对分布式缓存的直接依赖。在缓存失效时,首先查询本地缓存,如果没有,再查询分布式缓存。

2)针对热点缓存失效的情况:

  1. 设置随机的过期时间,在设定的基础上添加一个随机值,避免大量缓存同时失效。
  2. 缓存永不失效(不太推荐,实用性太差)。
  3. 缓存预热,在系统启动或者缓存失效前,通过后台任务将常用的数据加载到缓存中,防止缓存刚启动时产生大量数据库请求。

实现缓存预热

常见的缓存预热方式有两种:

  1. 使用定时任务,比如 xxl-job,来定时触发缓存预热的逻辑,将数据库中的热点数据查询出来并存入缓存中。
  2. 使用消息队列,比如 Kafka,来异步地进行缓存预热,将数据库中的热点数据的主键或者 ID 发送到消息队列中,然后由缓存服务消费消息队列中的数据,根据主键或者 ID 查询数据库并更新缓存。

数据一致性问题

什么情况下会出现数据一致性问题?

因为缓存的操作和数据库的操作是存在一定的时间差的。而且这两个操作是没办法保证原子性的,也就是说,是有可能一个操作成功,一个操作失败的。所以,这就必然会存在不一致的情况。

写写并发问题

先写数据库,再写缓存的情况。

WW
写数据库,更新成20
写数据库,更新成10
写缓存,更新成10
写缓存,更新成20**(数据不一致)**

先写缓存,再写数据库的情况。

WW
写缓存,更新成20
写缓存,更新成10
写数据库,更新成10
写数据库,更新成20**(数据不一致)**

读写并发问题

WR
读缓存,缓存中没有数据
读数据库,数据库中数据为10
写数据库,更新成20
写缓存,更新成10**(数据不一致)**

如何解决数据一致性问题

1.删除缓存还是更新缓存

  • 更新缓存更加复杂: 在缓存中,当数据不仅仅是简单的字符串(如JSON串或map类型),更新缓存变得更为复杂和易出错。例如,扣减库存时,需要从缓存中取出整个订单模型,反序列化后修改库存字段,再序列化并更新回缓存。相比之下,直接删除缓存再重建的策略更为简洁。
  • 更新缓存更容易出现数据不一致: 在“写写并发”的场景中,如果同时更新缓存和数据库,很容易会出现因为并发的问题导致数据不一致的情况。但是,如果是做缓存的删除的话,在写写并发的情况下,缓存中的数据都是要被清除的,所以就不会出现数据不一致的问题。
  • 删除缓存会出现Cache miss问题: 在删除缓存后的下一次查询会无法命中缓存,要查询一下数据库。这种cache miss在某种程度上可能会导致缓存击穿,也就是刚好缓存被删除之后,同一个Key有大量的请求过来,导致缓存被击穿,大量请求访问到数据库。但是,通过加锁的方式是可以比较方便的解决缓存击穿的问题的。

2.先删缓存还是先更新数据库(推荐先更新数据库)

  • 先删缓存

好处: 缓存删除之后,就不存在数据不一致的问题了。

坏处: 由于读写并发问题(当缓存不存在数据时,读线程读取数据库后需要写入缓存),先删缓存刚好可以让读线程读不到缓存,会加大这种可能性的发生。此外,缓存被删除后,更容易出现缓存击穿的问题。(此处引出缓存三大问题)

  • 先更新数据库

好处: 缓存删除失败的概率比较低,除非是网络问题或者缓存服务器宕机。先更新数据库,确保数据是最新的,即使缓存删除失败,数据出现短暂的不一致,在下次查询缓存时如果缓存过期,就会更新缓存,使得数据一致。(此处引出延迟双删,因为如果第一次缓存删除失败了,下一次查询时缓存没过期,就会查询到旧数据导致数据不一致)

坏处: 由于整个过程不是一个原子操作。一旦删除缓存失败,就会导致数据库中的数据已经更新,但是缓存还是旧数据,产生数据不一致。

延迟双删策略

@service
public class Productservice {
    @Autowired
	private stringRedisTemplate redisTemplate;
    // 定义阻塞队列
	private final BlockingQueuexstring> cacheDeletionQueue = new LinkedBlockingQueue<>();
	public void updateProduct(Product product) {
        //第一次删除缓存
		deletecache(product.getId());
		// 更新数据库
		updateProductInDB(product);
		// 第二次删除缓存,将缓存key存入阻塞队列
		cacheDeletionQueue.add(product.getId());
    }
	// 时间到后,从阻塞队列中取出key,删除缓存
    @scheduled(fixedDelay =100)
    public void delayedcacheDeletion(){
        String productId = cacheDeletionQueue.poll();
        if(productId != null) {
        	deletecache(productId);	//删除缓存
        }  
    }
}

为什么是删除缓存-->更新数据库-->删除缓存这个顺序?

  1. 防止读取到旧缓存数据
    • 先更新数据库,再删除缓存:假设先更新数据库再删除缓存,在删除缓存之前,可能有其他并发请求读取到旧的缓存数据。这些请求可能会在删除缓存之后将旧数据重新写入缓存,从而导致数据不一致。
    • 先删除缓存,再更新数据库:通过在更新数据库之前删除缓存,可以确保即使有并发请求,它们也会读取数据库的最新数据(因为缓存已被删除),从而写入新的缓存数据。
  2. 处理并发请求带来的数据不一致
    • 不做处理:在更新数据库之前,可能有其他并发请求读取了旧的数据库数据。这些请求可能会在更新数据库之后重新写入缓存,从而导致数据不一致。
    • 延迟删除缓存:延迟一段时间后再次删除缓存,确保缓存中不会存有旧数据。
      写入新的缓存数据。
  3. 处理并发请求带来的数据不一致
    • 不做处理:在更新数据库之前,可能有其他并发请求读取了旧的数据库数据。这些请求可能会在更新数据库之后重新写入缓存,从而导致数据不一致。
    • 延迟删除缓存:延迟一段时间后再次删除缓存,确保缓存中不会存有旧数据。

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

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

相关文章

【letcod-c++】128.最长连续序列

一、题目 二、分析 第一想法是像“242字母异位词”那样用哈希数组&#xff0c;但是这个数组元素的范围比较广&#xff0c;元素又比较分散&#xff0c;用数组太浪费空间&#xff0c;不合适。 于是考虑用哈希set(unordered_set),这个时候忽然想到前几天学习到set它能自动排序且自…

MySQL笔记(九):存储引擎

一、介绍 二、演示 Memory的使用场景&#xff1a; 例如网吧&#xff0c;用户再次上线时会更新状态 #表类型和存储引擎-- 查看所有的存储引擎SHOW ENGINES; -- 1、innodb 支持事务&#xff0c;外键&#xff0c;行级锁-- 2、myisam CREATE TABLE t31(id INT,name VARCHAR(32)) …

十二、享元模式

文章目录 1 基本介绍2 案例2.1 Digit 接口2.2 Color 枚举2.3 BigDigit 类2.4 DigitFactory 类2.5 Client 类2.6 Client 类的测试结果2.7 总结 3 各角色之间的关系3.1 角色3.1.1 Flyweight ( 抽象享元 )3.1.2 ConcreteFlyweight ( 具体享元 )3.1.3 UnsharedFlyweight ( 非享元 )…

2023/8/7 英语每日一段

There is unintended usefulness in this gentle enforcement of empathy. A mere news story makes it easy to deploy the defensive mechanism social scientists call “othering” which dismisses the victim, freak or dupe. But if it’s someone you have watched or …

文件上传绕过最新版安全狗

本文来源无问社区&#xff0c;更多实战内容&#xff0c;渗透思路可前往查看http://www.wwlib.cn/index.php/artread/artid/9960.html http分块传输绕过 http分块传输⼀直是⼀个很经典的绕过⽅式&#xff0c;只是在近⼏年分块传输⼀直被卡的很死&#xff0c;很多waf都开始加 …

数据科学 - 数据可视化(持续更新)

1. 前言​​​​​​​ 数据可视化能够将复杂的数据集转化为易于理解的图形、图表或图像。这种直观的表现形式使得人们能够更快地理解数据的分布、趋势、异常值以及数据之间的关系&#xff0c;从而更深入地洞察数据背后的信息。 数据可视化在数据分析和决策制定过程中具有不可…

【LLM基础知识】LLMs-Attention知识总结笔记v4.0

Attention机制 【1】简要介绍Attention机制 提出Attention的论文**&#xff1a;**Attention Is All You Need 论文地址&#xff1a;https://arxiv.org/pdf/1706.03762.pdf 提出Attention的背景&#xff1a;RNN处理序列数据时&#xff0c;token是逐个喂给模型的。比如在a3的位…

C++:map容器的使用

一、map的使用介绍 map文档介绍 1.1 map的模版参数 Key&#xff1a;键值对中Key的类型 T&#xff1a;键值对中value的类型 Compare&#xff1a;比较器的类型&#xff0c;map中的元素是按照Key来进行比较的&#xff0c;缺省情况&#xff08;不传参数时&#xff09;按照小于来…

健康读物:浮毛带来的危害竟这么大?去浮毛宠物空气净化器分享

前两天去我朋友家玩&#xff0c;进他家扑面而来的浮毛让我觉得呼吸都困难了不少&#xff0c;朋友说也有打扫&#xff0c;空气中的浮毛是真没辙&#xff0c;而且他觉得浮毛那么大又进不了肺部&#xff0c;对健康没啥危害&#xff0c;顶多吃几口猫毛&#xff0c;就没有处理。于是…

2024年7月国产数据库大事记-墨天轮

本文为墨天轮社区整理的2024年7月国产数据库大事件和重要产品发布消息。 目录 2024年7月国产数据库大事记 TOP102024年7月国产数据库大事记&#xff08;时间线&#xff09;产品/版本发布兼容认证代表厂商大事记排行榜新增数据库厂商活动相关资料 2024年7月国产数据库大事记 …

操作系统(七)深入理解Linux内核进程上下文切换

本文深入探讨了Linux内核中的进程上下文切换机制。作者韩传华首先解释了进程上下文的概念&#xff0c;包括虚拟地址空间和硬件上下文&#xff0c;并以Linux 5.0内核源码和ARM64架构为例进行讲解。文章详细介绍了进程上下文切换的两个主要过程&#xff1a;进程地址空间切换和处理…

PHP餐饮点餐系统小程序源码

&#x1f37d;️餐饮新纪元&#xff1a;揭秘高效点餐系统的魅力✨ &#x1f4f1;一键下单&#xff0c;快捷就餐新体验&#x1f680; 在这个快节奏的时代&#xff0c;谁不渴望在忙碌之余享受一顿快速而美味的餐食呢&#xff1f;餐饮点餐系统的出现&#xff0c;就像是为我们的餐…

Spring Boot实战:拦截器

一.拦截器快速入门 1.1了解拦截器 什么是拦截器&#xff1a; 概念 &#xff1a;拦截器是Spring框架提供的核⼼功能之⼀, 主要⽤来拦截⽤⼾的请求, 在指定⽅法前后, 根据业务需要执⾏预先设定的代码。 也就是说, 允许开发⼈员提前预定义⼀些逻辑, 在⽤⼾的请求响应前后执⾏. 也…

CoA:提升大型语言模型的多步推理能力

人工智能咨询培训老师叶梓 转载标明出处 大模型&#xff08;LLMs&#xff09;在处理复杂问题时&#xff0c;往往需要借助外部工具来获取现实世界知识&#xff0c;例如网络搜索、数学和物理规则等。然而现有的工具辅助语言模型在多步推理问题中调用工具时存在效率和准确性的挑战…

企业级敏捷框架:业务驱动型敏捷与产品需求团队

本文介绍了一种新的企业级敏捷框架——业务驱动型敏捷&#xff08;Business-driven Agile&#xff09;与 PRT&#xff08;Product Requirement Team&#xff09;&#xff0c;旨在解决传统敏捷方法中需求定义的瓶颈&#xff0c;从而提升产品价值并提高开发效率。原文: A new ent…

dynamic-datasource+Mybatis多数据源使用

Gitee地址:dynamic-datasource: 基于 SpringBoot 多数据源 动态数据源 主从分离 快速启动器 支持分布式事务 依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency&…

腾讯云AI代码助手:智驭Python,编织代码的诗篇 —— 深度测评体验

文章目录 引言&#x1f496;1. 开发环境介绍&#x1f4bb;vscode安装插件方法一&#xff1a;链接访问下载安装方法二&#xff1a;vscode直接安装 2. 使用实例✨1. &#x1f6e1;️代码补全&#xff0c;分秒必争2. &#x1f4a1; 技术对话&#xff0c;智慧碰撞3. &#x1f527; …

【分享】洁净室环境检测必测项目详细解读

环境监测&#xff08;Environmental monitoring&#xff09;在实现此目标中起着重要的作用——它提供了有关制造环境的关键信息&#xff0c;避免放行可能受污染的产品。 由于环境监测在制造过程中的重要性&#xff0c;相关机构围绕市场活动推出了许多法规要求和指南。这些标准随…

el-table自动滚动到最底部

我的需求是这样的&#xff0c;因为我的表格是动态的&#xff0c;可以手动新增行&#xff0c;固定表头&#xff0c;而且需要一屏显示&#xff0c;为了方便用户就需要再新增的时候表格自动向上滚动。 差了官方文档后发现有一个属性可以支持 这个属性正是自己需要的&#xff0c;所…

朵拉朵尚:坚持深耕护肤领域 荣获2023年度影响力品牌奖

朵拉朵尚&#xff1a;坚持深耕护肤领域 荣获2023年度影响力品牌奖 伴随着经济全球化的浪潮&#xff0c;新产业、新业态、新动能不断涌现&#xff0c;我国化妆品消费也迅速崛起&#xff0c;成为近年来化妆品行业发展增长速度最快的国家。1月30日&#xff0c;朵拉朵尚受邀参加快…