什么是分布式缓存

在实际开发场景中,往往单机应用无法满足当前的需求,需要对项目进行分布式部署,由此每个项目中的缓存都是属于自己独立服务的,并不能共享,其次当某个服务更新了缓存,其他服务并不知道,当用户请求到其他服务时,获取到的往往还是旧的数据。
这时就需要将缓存的数据放在一个统一的地方进行管理,如: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)"
allEntries allEntries是布尔类型的,用来表示是否需要清除缓存中的所有元素。默认值为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中是否有缓存数据:

维护缓存

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

扩展资料

Spring分布式缓存相关推荐

  1. java 项目做多级缓存_【开源项目系列】如何基于 Spring Cache 实现多级缓存(同时整合本地缓存 Ehcache 和分布式缓存 Redis)...

    一.缓存 当系统的并发量上来了,如果我们频繁地去访问数据库,那么会使数据库的压力不断增大,在高峰时甚至可以出现数据库崩溃的现象.所以一般我们会使用缓存来解决这个数据库并发访问问题,用户访问进来,会先从 ...

  2. Spring Cloud Alibaba微服务架构实战教程—17分布式缓存下Redis设计

    前言 大多数的文章,开头就是告诉你使用redis做缓存,怎么怎么样,而本系列,不打算采用这样无趣的写法,这和直接搬运有什么区别?笔者力求读者能得到更大程度的系统学习,会从为什么使用缓存来给大家进行学习 ...

  3. 分布式缓存的选择及问题

    现如今,缓存系统的应用非常广泛,能够用来提高并发数.数据吞吐量,提高快速响应能力.那么当数据量达到一定程度,单机环境可能就显得有些力不从心了,就需要一个分布式缓存系统.分布式缓存能够处理大量的动态数据 ...

  4. 阿里开源的缓存框架JetCache,实现spring二级缓存

    之前一直在用Spring Cache进行接口数据的缓存,主要是Spring Cache在对具体key缓存失效时间的设置不是很方法,还要自己去扩展,无意中发现了阿里的JetCache.大部分的需求都能满 ...

  5. Spring Boot 缓存应用实践

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源:cnblogs.com/jeffwongishan ...

  6. Redis 分布式缓存 Java 框架

    https://dzone.com/articles/java-distributed-caching-in-redis 为什么要在 Java 分布式应用程序中使用缓存? 在提高应用程序速度和性能上, ...

  7. EhCache 分布式缓存/缓存集群

    开发环境: System:Windows JavaEE Server:tomcat5.0.2.8.tomcat6 JavaSDK: jdk6+ IDE:eclipse.MyEclipse 6.6 开发 ...

  8. Spring+ehcache缓存实例

    一.ehcahe的介绍 EhCache 是一个纯Java的进程内缓存框架,具有快速.精干等特点,是Hibernate中默认的CacheProvider.Ehcache是一种广泛使用的开源Java分布式 ...

  9. 淘宝架构师为你揭秘2017双十一分布式缓存服务Tair

    1 问题背景 分布式缓存一般被定义为一个数据集合,它将数据分布(或分区)于任意数目的集群节点上.集群中的一个具体节点负责缓存中的一部分数据,整体对外提供统一的访问接口[1].分布式缓存一般基于冗余备份 ...

最新文章

  1. 反弹c语言作业,C语言实现反弹球小游戏
  2. openwrt 遍译php_完全新手教程:编译openwrt全过程
  3. 函数式编程语言python-写 Python 代码不可不知的函数式编程技术
  4. HDU 4628 Pieces(DP + 状态压缩)
  5. Java:内部类之成员内部类,内部类之匿名内部类
  6. PEInfo编程思路讲解03 - 工具篇03|解密系列
  7. Unix下的crontab简介
  8. Java语言与sikuli配合
  9. 海岸鸿蒙2018年标准物质,海岸鸿蒙——20年权威的标准物质研制单位
  10. nginx代理配置根据ip地址来转发到不同的地址端口
  11. c语言数组转指针,(转)c语言指针数组
  12. 如何给计算机关闭网络连接,win10系统的电脑怎么把网络连接关闭?
  13. 谷歌浏览器如何正确离线网页
  14. 2023年东北大学外国语言学及应用语言学考研上岸经验贴
  15. c# WPF中通过双击编辑DataGrid中Cell的示例(附源码)
  16. exe软件如何更改标题?
  17. ThinkPad E431 Bluetooth驱动
  18. Mac突然连接不上WiFi的问题
  19. JQuery源码分析 - 闭包机制在jQuery中的使用及冲突解决
  20. 由递推关系式用差分方程的方法得到通项公式实现求斐波那契数列的第n项;迭代、递归、栈、差分方程之间的本质联系以及由推广的迭代法解决“变态青蛙跳台阶”问题;汉诺塔问题的数字特征以及用递归解决的原理推导。

热门文章

  1. 【捷凡阁】带你分享几个赚钱小连招
  2. 地图格子 java_地图纠偏java算法
  3. 测试cpu玩游戏的软件,玩游戏时显卡和CPU到底哪个更重要?来看下面的测试你就会明白了...
  4. Google Play应用广告该如何运作
  5. python爬虫 出现built in method strip of str object at ox000002268861D480
  6. vmospro启动黑屏_VMOS Pro,安卓手机上的虚拟机
  7. 信息化、数字化与数字化转型的区别
  8. http/https等爬虫代理ip的基本实现原理
  9. 2023大数据面试真题(持续更新)
  10. 梁斌penny_Penny捏在云中:提升和转变与应用服务-当您不需要云中的VM时