Spring分布式缓存

article/2025/9/21 21:17:57

什么是分布式缓存

在实际开发场景中,往往单机应用无法满足当前的需求,需要对项目进行分布式部署,由此每个项目中的缓存都是属于自己独立服务的,并不能共享,其次当某个服务更新了缓存,其他服务并不知道,当用户请求到其他服务时,获取到的往往还是旧的数据。
这时就需要将缓存的数据放在一个统一的地方进行管理,如:redis

注解介绍

Spring为我们提供了三大注解@Cacheable@CachePut@CacheEvict可在绝大部分场景下优雅实现分布式缓存。

@EnableCaching(启用缓存)

一般注解在启动类或配置类上,表示启用缓存,使@Cacheable@CachePut@CacheEvict等注解生效。

@Cacheable(添加/获取缓存)

属性名作用
cacheNames/value缓存名(必须指定至少一个值),即缓存命名空间名称,用于确定缓存目标。
key缓存key,缺省为按照方法的所有入参进行组合,可以使用SpEL表达式,实际存储key时默认以cacheNames/value作为前缀。
keyGenerator指定key的生成器生成键值key(与key二选一),非必需。
cacheManager指定缓存管理器(例如ConcurrentHashMap、Redis等),非必需。
cacheResolver和cacheManager作用一样,使用时二选一,非必需。
condition指定缓存的条件(对参数判断,满足什么条件时才缓存),可用SpEL表达式,例如:方法入参为对象user则表达式可以写为condition = "#user.age>18",表示当入参对象user的属性age大于18才进行缓存。
unless否定缓存的条件(对结果判断,满足什么条件时不缓存),即满足unless指定的条件时,对调用方法获取的结果不进行缓存,例如:unless = "result==null",表示如果结果为null时不缓存。
sync是否使用异步模式进行缓存,默认false。

cache_test作为缓存名,参数id作为缓存key,如果命中缓存,直接返回结果。如果缓存不存在,就执行方法逻辑,并将方法的返回结果缓存。

@Cacheable(value = "cache_test", key = "#id")
@GetMapping("/get")
public Result<?> get(Long id) {return R.success(jdbcDAO.get(id));
}

@CachePut(更新缓存)

属性名作用与描述
cacheNames/value缓存名(必须指定至少一个值),即缓存命名空间名称,用于确定缓存目标。
key缓存key,缺省为按照方法的所有入参进行组合,可以使用SpEL表达式,实际存储key时默认以cacheNames/value作为前缀。
keyGenerator指定key的生成器生成键值key(与key二选一),非必需。
cacheManager指定缓存管理器(例如ConcurrentHashMap、Redis等),非必需。
cacheResolver和cacheManager作用一样,使用时二选一,非必需。
condition指定缓存的条件(对参数判断,满足什么条件时才缓存),可用SpEL表达式,例如:方法入参为对象user则表达式可以写为condition = "#user.age>18",表示当入参对象user的属性age大于18才进行缓存。
unless否定缓存的条件(对结果判断,满足什么条件时不缓存),即满足unless指定的条件时,对调用方法获取的结果不进行缓存,例如:unless = "result==null",表示如果结果为null时不缓存。

cache_test作为缓存名,参数id作为缓存key,当方法逻辑执行完之后,将返回结果进行覆盖缓存。

@CachePut(value = "cache_test", key = "#userGroupIPO.id")
@PutMapping("/put")
public Result<?> put(@Validated UserGroupIPO userGroupIPO) {jdbcDAO.updateById(Convert.toJSONObject(userGroupIPO));return R.success(jdbcDAO.get(userGroupIPO.getId()));
}

@CacheEvict(删除缓存)

属性名作用与描述
cacheNames/value缓存名(必须指定至少一个值),即缓存命名空间名称,用于确定缓存目标。
key缓存key,缺省为按照方法的所有入参进行组合,可以使用SpEL表达式,实际存储key时默认以cacheNames/value作为前缀。
keyGenerator指定key的生成器生成键值key(与key二选一),非必需。
cacheManager指定缓存管理器(例如ConcurrentHashMap、Redis等),非必需。
cacheResolver和cacheManager作用一样,使用时二选一,非必需。
condition指定删除缓存的条件(对参数判断,满足什么条件时才删除缓存),可用SpEL表达式,例如:入参为字符userId的方法删除缓存条件设定为当入参不是user001就删除缓存,则表达式可以写为condition = "!('user001').equals(#userId)"
allEntriesallEntries是布尔类型的,用来表示是否需要清除缓存中的所有元素。默认值为false,表示不需要。当指定allEntries为true时,Spring Cache将忽略指定的key,清除缓存中的所有内容。
beforeInvocation清除操作默认是在对应方法执行成功后触发的(beforeInvocation = false),即方法如果因为抛出异常而未能成功返回时则不会触发清除操作。使用beforeInvocation属性可以改变触发清除操作的时间。当指定该属性值为true时,Spring会在调用该方法之前清除缓存中的指定元素。

cache_test作为缓存名,参数id作为缓存key,当方法逻辑执行完之后,删除缓存。

@CacheEvict(value = "cache_test", key = "#id")
@DeleteMapping("/delete")
public Result<?> delete(@RequestParam("id") Long id) {jdbcDAO.deleteLogic(id);return R.success();
}

@CacheConfig

注解在类上面,可使@Cacheable@CachePut@CacheEvict注解无需定义重复的:cacheNames/value、keyGenerator、cacheManager、cacheResolver属性值

@CacheConfig(cacheNames = "cache_test")
@RestController
@RequestMapping("/cache")
public class CacheController {@AutowiredJdbcDAO jdbcDAO;@Cacheable(key = "#id") // 无需重复指定 cacheNames/value 属性@GetMapping("/get")public Result<?> get(Long id) {return R.success(jdbcDAO.get(id));}}

@Caching

用于将@Cacheable、@CachePut、@CacheEvict这三个注解所提供的能力自由组合,如查询用户时缓存不应该只放id → user,应该连同 cellphone → user、email → user一起放入,这样下次如果按照cellphone或email来查询时,也可从缓存中命中了。

@Caching(cacheable = {@Cacheable(value = "cache_test", key = "#id")},put = {@CachePut(value = "cache_test", key = "#result.cellphone", condition = "#result != null"),@CachePut(value = "cache_test", key = "#result.email", condition = "#result != null")}
)
public UserDO getUserDO(Long id) {return jdbcDAO.get(id);
}

开始使用

引入依赖

引入spring-boot-starter-data-redis依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

启用缓存

在启动类中加上@EnableCaching注解开启缓存

@EnableCaching
@SpringBootApplication
public class TestApplication {public static void main(String[] args) throws Exception {SpringApplication.run(TestApplication.class, args);}}

使用缓存

@Cacheable(value = "cache_test", key = "#id")
@GetMapping("/get")
public Result<?> get(Long id) {System.out.println("未命中Redis缓存,使用JDBC去数据库查询数据。");return R.success(jdbcDAO.get(id));
}

测试

http://localhost:8080/cache/get?id=18

第一次访问-控制台打印结果:

2021-06-05 19:15:34.041  INFO 44320 --- [nio-8080-exec-1] ai.yue.library.test.aspect.HttpAspect    : requestIp=0:0:0:0:0:0:0:1
2021-06-05 19:15:34.041  INFO 44320 --- [nio-8080-exec-1] ai.yue.library.test.aspect.HttpAspect    : requestUri=/cache/get
2021-06-05 19:15:34.041  INFO 44320 --- [nio-8080-exec-1] ai.yue.library.test.aspect.HttpAspect    : requestMethod=GET
2021-06-05 19:15:34.041  INFO 44320 --- [nio-8080-exec-1] ai.yue.library.test.aspect.HttpAspect    : requestHandlerMethod=ai.yue.library.test.controller.data.redis.CacheController.get()
未命中Redis缓存,使用JDBC去数据库查询数据。
2021-06-05 19:54:11.332 DEBUG 44320 --- [nio-8080-exec-4] druid.sql.Statement                      : {conn-310002, pstmt-320001} executed.
select * from `user` where 1 = 1 and `id` = 18

第一次访问-响应数据:

{"code": 200,"msg": "成功","flag": true,"count": null,"data": {"id": 18,"sortIdx": null,"deleteTime": 0,"createTime": "2018-07-18 09:10:46","updateTime": "2021-06-03 14:29:41","cellphone": "185****6311","email": null,"password": "***********************************************","nickname": "张三丰3","sex": "男"}
}

第二次访问:结果与第一次相同,但未打印出查询日志,因此证明响应的结果是取的Redis缓存数据,而不是执行的JDBC查询。同时我们也确认下Redis中是否有缓存数据:

确认Redis缓存

维护缓存

上面我们介绍了如何使用@Cacheable注解添加与获取缓存,实际场景中我们还需要更新缓存@CachePut与删除缓存@CacheEvict
开发者需要结合业务情况,在需要操作到缓存相关数据时,进行缓存数据同步,也就是更新或删除缓存,需求多变灵活运用。

扩展资料

👉示例源码

👉Redis缓存雪崩、击穿、穿透、预热、降级详解


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

相关文章

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

又是一个没有开工红包的公司&#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;目的是让用户的访问速…

分布式缓存灵魂十连,你能坚持几个?

点击上方蓝色“方志朋”&#xff0c;选择“设为星标” 回复“666”获取独家整理的学习资料&#xff01; 目录 前言 目前工作中用到的分布式缓存技术有redis和memcached两种&#xff0c;缓存的目的是为了在高并发系统中有效降低DB的压力&#xff0c;但是在使用的时候可能会因为缓…

Webform 常用控件

Webform 常用控件 一&#xff0c;简单控件 1&#xff0c;Lable——标签&#xff1a;在网页中呈现出来的时候会变成span标签 属性&#xff1a;Text——标签上的文字 BackColor&#xff0c;ForeColor——背景色&#xff0c;前景色 Font——字体 Bold-加粗 Italic-倾斜 Under…

Web窗体(WebForm)

一&#xff0e;简介 0. 页面的生命周期。 1. WebForm后台页面类继承于Page类&#xff0c;Page类实现了IHttpHandler接口。 2. 前台页面类继承于后台页面类。 3. 先调用PageLoad方法&#xff0c;再调用Render方法生成html代码。 二. 加密安全 互联网没有绝对的安全&#xff0c;登…

ASP.NET Web Form学习

ASP.NET Web Form学习 0.aspx与html 它如何工作&#xff1f; 从根本上讲&#xff0c;ASP.NET 页面与 HTML 完全相同。 HTML 页面的扩展名是 .htm 或 .html。假如浏览器从服务器请求某张 HTML 页面&#xff0c;服务器不进行任何修改&#xff0c;就会把该页面发往浏览器。 A…

forms.Form和forms.ModelForm

forms.ModelForm是forms.Form的升级版 forms.Form验证规则 2.1 forms.py 2.2 view.py 把我们写的UserResetForm导入到view.py 2.3 模板 forms.ModelForm验证规则 3.1 models.py 3.2 forms.py就用上面模型类里面的验证规则 3.3 view.py 3.4 模板看你实际的情况 forms.…

WebForm与MVC混用

在现有的WebForm项目中加入MVC&#xff0c;可以吗&#xff1f; 西蒙说&#xff0c;可以。 怎么加呢&#xff1f; 我的开发环境是&#xff1a;WIN7 IIS7.5 VS2012 一、WebForm项目添加引用&#xff1a; 我都是选了最高的版本。 二、将MVC项目的部分文件拷贝到WEBFORM项目 …