分布式缓存解决方案

article/2025/9/21 20:57:47

高并发首选:缓存

项目代码下载地址:https://gitee.com/tyytx/distrbute-demo.git
接口高并发的解决思路:1、加缓存 2、数据静态化 3、集群 4、分布式 5、同步转异步 6、限流、降级
适合加缓存的场景:读多写少的数据,不经常需要修改的数据、一致性要求不高(数据只能保持最终一致性,不能保证数据同步一致性)

缓存的概念

1)外存

外存储器是指除计算机内存及CPU缓存以外的存储器,断电后仍然能保存数据。常用的有硬盘、u盘等。

2)内存

内存是计算机组成部分。被称为内存存储器,其作用是暂时存放CPU运算数据,以及与外存之间交互的存储器。只要计算机在运行中,CPU就会把需要运算的数据读到内存中,当运算完成后CPU在将运算结果写到外存中。断电后数据就会清空,常用的有内存条。

3)缓存

缓存就是介于内存和外存之间的存储,加快内存和外存之间的数据交互。软件中间件一般用外部计算机内存当缓存。

4)缓存的指标

1)缓存命中率
从缓存中读取数据的次数与总读取次数的比率,命中率越高越好。
2)缓存更新策略
如果缓存满了,从缓存中清除数据的策略。常见的有:LFU、LRU、FIFO
LRU(Least Recently Used):最久未使用算法,使用时间距离现在最久的那个被移除。
LFU(Least Frequencyly Used):最少使用算法,一定时间使用次数最少的那个被移除。
FIFO(First In First Out):先进先出算法,先放入缓存的先被移除。

Redis缓存在Java中的实现

在java代码中,我们一般对调用的方法进行缓存控制,比如我要查询具体的省市区县数据,findProvidenceAndCityAndArea(String id),那么我们应该在调用这个方法之前先从缓存中查找有没有这个数据,如果没有在调用该方法从数据库中查询,然后添加到缓存中去;下次接口调用时将会从缓存直接获取数据。Java中使用分布式缓存Redis。

1)缓存逻辑流程图

查询策略:
在这里插入图片描述
修改策略:
在这里插入图片描述

2)逻辑流程代码

查询代码:

/*** 使用缓存,根据省份ID获取省市数据* @param provinceid* @return*/@Overridepublic Provinces detail(String provinceid) {Provinces provinces = null;//1、在redis查询provinces = (Provinces)redisTemplate.opsForValue().get(provinceid);//2.如果命中,则返回缓存数据if (null != provinces){//redisTemplate.expire(provinceid,20000, TimeUnit.MILLISECONDS);System.out.println("缓存中得到数据");return provinces;}//3.如果没有命中,则去数据库中读取数据provinces = super.detail(provinceid);if (null != provinces){//4.将查询出来的数据写入缓存redisTemplate.opsForValue().set(provinceid,provinces);//set缓存//redisTemplate.expire(provinceid,20000, TimeUnit.MILLISECONDS);//设置过期}return provinces;}

修改策略采用双删策略:

@Overridepublic Provinces update(Provinces entity) {//双删,防止多线程操作之间的时间间隔导致数据不一致redisTemplate.delete(entity.getProvinceid());//直接删除缓存,预防数据库成功,缓存失败super.update(entity);redisTemplate.delete(entity.getProvinceid());//双删return entity;}@Overridepublic Provinces add(Provinces entity) {redisTemplate.delete(entity.getProvinceid());super.add(entity);redisTemplate.delete(entity.getProvinceid());//双删return entity;}@Overridepublic void delete(String provinceid) {redisTemplate.delete(provinceid);super.delete(provinceid);redisTemplate.delete(provinceid);//双删}

SpringCache的用法

因为缓存代码逻辑有一定的固定性,所以可以利用模板设计模式对其进行封装。SpringCache并非具体的缓存技术,而是对各种缓存中间件的封装,具体的底层缓存是EhCache还是Redis,只需要简单的配置就可以了。

设计理念

正如Spring框架的其他服务一样,Spring Cache首先是提供了一层抽象,和兴抽象主要体现在这俩个接口上:
在这里插入图片描述

org.springframework.cache.Cache: 代表缓存本身
org.springframework.cache.CacheManager:代表对缓存的处理和管理
抽象的意义在于屏蔽实现细节的差异和提供扩展性,Cache的抽象解耦了缓存的使用和缓存的后端存储,方便后续更换存储。

使用SpringCache

在这里插入图片描述
使用示例:
1)声明缓存:在方法上添加@cacheable等注解,表示缓存该方法的结果。

    @Cacheable// value指定当前接口,要使用哪一个缓存器 --- 如果该缓存器不存在,则创建一个public Provinces detail(String provinceid) {//一个接口方法,对应一个缓存器return super.detail(provinceid);}

2)开启Spring的cache功能
<cache:annotation-driven /> 或者使用注解@EnableCaching 的方式

@Configuration
@EnableCaching
public class CacheConfig {
}

3)配置后端的存储
1)JDK内存作为缓存

    @Beanpublic CacheManager cacheManager() {//jdk里,内存管理器SimpleCacheManager cacheManager = new SimpleCacheManager();cacheManager.setCaches(Collections.singletonList(new ConcurrentMapCache("province")));return cacheManager;}

RedisCacheManager

 @Beanpublic CacheManager cacheManager(RedisConnectionFactory connectionFactory) {return RedisCacheManager.builder(connectionFactory).cacheDefaults(RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(20))) //缓存时间绝对过期时间20s.transactionAware().build();}

注解风格说明

1)@Cacheable:查询方法

@Cacheable// value指定当前接口,要使用哪一个缓存器 --- 如果该缓存器不存在,则创建一个public Provinces detail(String provinceid) {//一个接口方法,对应一个缓存器return super.detail(provinceid);}

2)@CachePut:新增或者修改方法

//这个AOP,先修改数据库,在删除缓存@CachePut(key = "#entity.provinceid")public Provinces update(Provinces entity) {return super.update(entity);}

3)@CacheEvict:删除缓存

@CacheEvictpublic void delete(String provinceid) {super.delete(provinceid);}

缓存穿透解决方案

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起Id为“-1”的数据或者id为不存在的数据。这是的用户很可能是恶意攻击,攻击会导致数据库压力过大。
在这里插入图片描述

解决方案:使用布隆过滤器

缓存击穿解决方案

缓存击穿是指缓存中没有,但是数据库中有的数据(一般是缓存过期),这是由于并发用户特别多,同时读取缓存没读取到数据,又同时去数据库读取数据,一期数据库压力瞬间增大,造成过大压力。
在这里插入图片描述

private BloomFilter<String> bf =null; //等效成一个set集合/*** 在bean初始化完成后,实例化bloomFilter,并加载数据*/@PostConstruct //对象创建后,自动调用本方法public void init(){List<Provinces> provinces = this.list();//当成一个SET----- 占内存,比hashset占得小很多bf = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), provinces.size());// 32个for (Provinces p : provinces) {bf.put(p.getProvinceid());}}@Cacheable(value = "province")public Provinces detail(String provinceid) {//先判断布隆过滤器中是否存在该值,值存在才允许访问缓存和数据库if(!bf.mightContain(provinceid)){System.out.println("非法访问--------"+System.currentTimeMillis());return null;}System.out.println("数据库中得到数据--------"+System.currentTimeMillis());Provinces provinces = super.detail(provinceid);return provinces;}

缓存雪崩解决方案

缓存血本是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至宕机。和缓存击穿不同的是,缓存击穿指并发查询同一条数据,缓存雪崩是不同数据同时过期,很多数据都要从数据库在这里插入图片描述
查询。

解决方案:加显示锁(可以解决缓存击穿问题)

public Provinces detail(String provinceid) {// 1.从缓存中取数据Cache.ValueWrapper valueWrapper = cm.getCache(CACHE_NAME).get(provinceid);if (valueWrapper != null) {logger.info("缓存中得到数据");return (Provinces) (valueWrapper.get());}//2.加锁排队,阻塞式锁---100个线程走到这里---同一个sql的取同一把锁doLock(provinceid);//32个省,最多只有32把锁,1000个线程try{//第二个线程进来了// 一次只有一个线程//双重校验,不加也没关系,无非是多刷几次库valueWrapper = cm.getCache(CACHE_NAME).get(provinceid);//第二个线程,能从缓存里拿到值if (valueWrapper != null) {logger.info("缓存中得到数据");return (Provinces) (valueWrapper.get());//第二个线程,这里返回}Provinces provinces = super.detail(provinceid);// 3.从数据库查询的结果不为空,则把数据放入缓存中,方便下次查询if (null != provinces){cm.getCache(CACHE_NAME).put(provinceid, provinces);}return provinces;}catch(Exception e){return null;}finally{//4.解锁releaseLock(provinceid);}}

缓存的数据一致性解决策略

数据更新就会引起数据库的更新和缓存更新,就容易出现缓存(Redis)和数据库(Mysql)间的数据一致性问题。无论哪种解决方案都有缺点,没有完美的解决方案。
在这里插入图片描述

1)数据实时同步更新:代码同步调用Cache增删改

优点:数据实时同步更新,保持强一致性
缺点:代码耦合,对业务代码有侵入性

2)数据准实时更新:mq异步更新数据

优点:数据同步有较短延迟 ,与业务解耦
缺点:实现复杂,架构较重,中间件的稳定性

3)缓存失效机制:缓存失效

优点:实现简单,无须引入额外逻辑
缺点:有一定延迟,存在缓存击穿/雪崩问题

4)定时任务更新:定时任务,最终数据一致性

优点:不影响正常业务
缺点:不保证一致性,依赖定时任务


http://chatgpt.dhexx.cn/article/HF7ooqX8.shtml

相关文章

ehcache作为分布式缓存的研究

ehcache支持两种拓扑结构&#xff0c;一种是Distributed Caching&#xff0c;另一种是Replicated Caching Distributed Caching 这和一般意义上的分布式缓存非常类似&#xff0c;这一类型的缓存是有client-server之分的&#xff0c;application通过client向server端请求缓存数据…

分布式缓存平台方案

目录 1、总体说明 1.1、采取redis的原因 1.2、平台总体架构 2、多协议支持方案 3、高可用&#xff08;HA&#xff09;方案 3.1、基础设施 3.2、故障检测 3.3、故障切换 4、分布式方案 4.1、垂直扩展 4.2、水平扩展 5、容量管理 6、安全 6.1、配置安全 6.2、访问安…

分布式缓存架构基础

一、缓存概述 1.1 什么是缓存 缓存在wiki上的定义&#xff1a;用于存储数据的硬件或软件的组成部分&#xff0c;以使得后续更快访问相应的数据。缓存中的数据可能是提前计算好的结果、数据的副本等。典型的应用场景&#xff1a;有cpu cache, 磁盘cache等。本文中提及到缓存主要…

基于redis分布式缓存实现

简单说明下&#xff0c;写此文章算是对自己近一段工作的总结&#xff0c;希望能对你有点帮助&#xff0c;同时也是自己的一点小积累。 一.为什么选择redis 在项目中使用redis做为缓存&#xff0c;还没有使用memcache,考虑因素主要有两点&#xff1a; 1.redis丰富的数据结构,其…

本地缓存与分布式缓存

更多内容&#xff0c;前往 IT-BLOG 一般而言&#xff0c;现在互联网应用&#xff08;网站或App&#xff09;的整体流程&#xff0c;可以概括如图所示&#xff0c;用户请求从界面&#xff08;浏览器或App界面&#xff09;到网络转发、应用服务再到存储&#xff08;数据库或文件系…

Spring分布式缓存

什么是分布式缓存 在实际开发场景中&#xff0c;往往单机应用无法满足当前的需求&#xff0c;需要对项目进行分布式部署&#xff0c;由此每个项目中的缓存都是属于自己独立服务的&#xff0c;并不能共享&#xff0c;其次当某个服务更新了缓存&#xff0c;其他服务并不知道&…

高性能分布式缓存的设计原理

又是一个没有开工红包的公司&#xff01;&#xff01;&#xff01; 问题分析 通过以上对话&#xff0c;各位是否能够猜到所有缓存穿透的原因呢&#xff1f;回答之前我们先来看一下缓存策略的具体代码 缓存服务器IPhash(key)%服务器数量 这里还要多说一句&#xff0c;key的取值…

redis 分布式缓存 详解

1、Redis概述 1.1、NoSQL NoSQL(Not Only SQL)&#xff0c;意即不仅仅是SQL, 泛指非关系型的数据库。 1.2、Redis安装 首先需要从Redis官网上下载Redis的源码包&#xff0c;将下载的包上传到Linux&#xff0c;之后将gz文件进行解压。 # 解压gz文件 tar -zxvf redis-6.2.6…

本地缓存、分布式缓存以及多级缓存

像MySql等传统的关系型数据库已经不能适用于所有的业务场景&#xff0c;比如电商系统的秒杀场景&#xff0c;APP首页的访问流量高峰场景&#xff0c;很容易造成关系型数据库的瘫痪&#xff0c;随着缓存技术的出现很好的解决了这个问题。 一、缓存的概念&#xff08;什么是缓存…

分布式架构系列:缓存

一、缓存概述 缓存是分布式系统中的重要组件&#xff0c;主要解决高并发&#xff0c;大数据场景下&#xff0c;热点数据访问的性能问题。提供高性能的数据快速访问。 1.1缓存的原理 &#xff08;1&#xff09; 将数据写入/读取速度更快的存储&#xff08;设备&#xff09;&…

分布式缓存那些事儿

在前面的一些文章中&#xff0c;从实战的角度&#xff0c;讲解了有关memcached的应用、容灾、监控等等。但是缺乏对理论的讲解和原理性的剖析。本文将从理论的角度去介绍&#xff0c;让大家从宏观上对“分布式缓存、nosql”等技术有所了解&#xff0c;以便进一步学习和使用。在…

分布式缓存和本地缓存的区别

分布式缓存和本地缓存的区别 redis/memcached**分布式缓存**和map/guava**本地缓存**的区别什么是缓存一致性&#xff1f; redis/memcached分布式缓存和map/guava本地缓存的区别 缓存分为本地缓存和分布式缓存&#xff0c;使用map或guava的是本地缓存&#xff0c;轻量而快速&a…

分布式数据:缓存技术

分布式数据&#xff1a;缓存技术 前言什么是分布式缓存&#xff1f;Redis 分布缓存原理Memcached 分布式缓存原理对比分析知识扩展&#xff1a;除了分布式存储中的缓存&#xff0c;还有计算机体系结构和网络中的缓存&#xff0c; 它们又分别是什么呢&#xff1f;总结 前言 分布…

【分布式缓存】分布式缓存-缓存技术

目录 从数据的使用说起本地缓存远程缓存缓存策略缓存常见问题总结回顾与作业实践 1. 从数据的使用说起 我们把数据的使用频率和方式分个类 静态数据&#xff1a;一般不变&#xff0c;类似于字典表 准静态数据&#xff1a;变化频率很低&#xff0c;部门结构设置&#xff0c;…

分布式缓存详解

“ 今天无聊来撩一下分布式缓存&#xff0c;希望你们喜欢~ 编者荐语&#xff1a; 此篇文章对于分布式缓存讲解的非常透彻&#xff01; 目录 前言一. 常用的两种缓存技术的服务端特点1. Memcache服务端2. Redis服务端 二. 缓存结构化选型三. Redis构造大索引回源问题四. 一致性问…

分布式缓存的基本原理

随着互联网的发展&#xff0c;用户规模和数据规模越来越大&#xff0c;对系统的性能提出了更高的要求&#xff0c;缓存就是其中一个非常关键的组件&#xff0c;从简单的商品秒杀&#xff0c;到全民投入的双十一&#xff0c;我们都能见到它的身影。 分布式缓存首先也是缓存&…

分布式缓存

本文介绍关于缓存的常用设计模式。以及如何保证缓存的一致性进行分类讨论。 还会介绍关于缓存失效的常见问题&#xff0c;以及针对缓存失效的解决方法。 在高并发的环境下&#xff0c;比如春节抢票大战&#xff0c;一到放票的时间节点&#xff0c;分分钟大量用户以及黄牛的各种…

详解分布式系统的缓存设计

作者&#xff1a;vivo互联网服务器团队-Zhang Peng ​ 一、缓存简介 1.1 什么是缓存 缓存就是数据交换的缓冲区。缓存的本质是一个内存 Hash。缓存是一种利用空间换时间的设计&#xff0c;其目标就是更快、更近&#xff1a;极大的提高。 将数据写入/读取速度更快的存储&#xf…

今天带你了解-分布式缓存(一)

在网站架构的衍化历程中&#xff0c;当网站遇到性能瓶颈时&#xff0c;首先想到的解决方案就是使用缓存。 缓存指将数据存储在较高访问速度的存储介质中&#xff0c;以供系统处理。一方面缓存访问速度快&#xff0c;可以减少数据的访问时间&#xff0c;另一方面如果缓存的数据…

深入浅出分布式系统中的缓存架构

缓存&#xff0c;已经是一个老生常谈的技术了&#xff0c;在高并发读的情况下对于读服务来说可谓是抗流量的银弹。 高并发三大利器&#xff1a;缓存、限流、降级。 今天我们就来谈谈缓存。对于缓存&#xff0c;我的理解是让数据更接近于用户&#xff0c;目的是让用户的访问速…