SpringCache 缓存 - @Cacheable、@CacheEvict、@CachePut、@Caching、CacheConfig 以及优劣分析

 

目录

SpringCache 缓存

环境配置

1)依赖如下

2)配置文件

3)设置缓存的 value 序列化为 JSON 格式

4)@EnableCaching 

实战开发

@Cacheable

@CacheEvict

@CachePut

@Caching

@CacheConfig

SpringCache 的优势和劣势

读操作(优势)

写操作(劣势)

总结


SpringCache 缓存


环境配置

1)依赖如下

父依赖 SpringBoot 3.2.5

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

2)配置文件

spring:cache:type: redisredis:time-to-live: 3600000# key-prefix: CACHE_use-key-prefix: true cache-null-values: true
  • time-to-live: 3600000 -> 缓存过期时间,单位毫秒,此处相当于 1 小时(实际上也就解决了雪崩问题,因为一般设置每一个缓存时的时间线不一样)
  • key-prefix: CACHE_ -> 缓存 key 前缀(一般不用这个属性,而是使用分区名作为 key 前缀)
  • use-key-prefix: true -> 是否使用缓存分区名作为 key 前缀(分区名在 @Cacheable 中指定),建议为 true
  • cache-null-values: true -> 是否缓存空值(解决缓存穿透问题),建议为 true

3)设置缓存的 value 序列化为 JSON 格式

import org.springframework.boot.autoconfigure.cache.CacheProperties
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.cache.annotation.EnableCaching
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.redis.cache.RedisCacheConfiguration
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
import org.springframework.data.redis.serializer.RedisSerializationContext
import org.springframework.data.redis.serializer.StringRedisSerializer@Configuration
@EnableConfigurationProperties(CacheProperties::class) //让配置文件中的配置生效
@EnableCaching // 开启 SpringCache 缓存功能(如果这里不写这个注解,启动类上也一定要有!!!)
class MyCacheConfig {@Beanfun redisCacheConfiguration(cacheProperties: CacheProperties): RedisCacheConfiguration {//这里源码怎么写,咱们咱们写(只需要改一下缓存 value 的序列化方式即可)var config = RedisCacheConfiguration.defaultCacheConfig()//设置 key value 的序列化方式config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer()))config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(GenericJackson2JsonRedisSerializer()))val redisProperties = cacheProperties.redis//将配置文件中的所有配置都生效redisProperties.timeToLive?.let {config = config.entryTtl(it)}redisProperties.keyPrefix?.let {config = config.prefixCacheNameWith(it)}if (!redisProperties.isCacheNullValues) {config = config.disableCachingNullValues()}if (!redisProperties.isUseKeyPrefix) {config = config.disableKeyPrefix()}return config}}

4)@EnableCaching 

@EnableCaching 表示开启 SpringCache 缓存功能,加在 启动类 或者 配置类 上都可以.

实战开发

@Cacheable

a)使用说明:

@Cacheable 用来将方法的返回值数据保存到缓存中.

常用属性如下:

  • value:表示将当前缓存数据放到哪个 缓存组 中(可以理解为放到哪个文件夹下). 
    • 例如 @Cacheable(value = ["user"])
  • key:指定 key 是什么.  接受一个 SpEL 表达式,例如如下表格中的示例
    • 例如方法名作为 key:@Cacheable(value = ["user"], key = "#root.method.name")
    • 另外,如果不想使用 SpEL 表达式,可以直接在双引号内加上一对单引号,例如 key 为 "userinfo":@Cacheable(value = ["user"], key = "'userinfo'")
  • condition:条件判断属性,只有符合条件才可以被缓存.
    • 例如方法参数中的 id > 0 返回值才能被缓存 @Cacheable(value = ["user"], key = "#root.method.name", condition = "#id > 0")
  • sync:是否为同步执行. 如果设置为 true,会加锁(本地锁),可以用来解决击穿问题.

b)案例如下:

例如通过 SpEL表达式设置 缓存的 key 为 动态的id + "userinfo" ,

    @Cacheable(value = ["user"], key = "#id + 'userinfo'")override fun getUserinfo(id: Long): UserinfoVo {//业务逻辑...println("查询数据库...")return UserinfoVo( // 这里的 UserinfoVo 必须要有无参构造才行,否则缓存将来读取的时候会报错id = id,name = "cyk",age = 21,)}

第一查询之后,就可以看到 Redis 上面已经存在该数据.  之后的只要缓存未过期,都会直接查缓存.

@CacheEvict

a)使用说明

@CacheEvict 用来将数据从缓存中删除. 

他常常被用来实现 “失效模式” 来解决缓存一致性问题(数据库中的数据被更新之后,直接删除缓存上的数据即可,下次查询的时候,自动同步到缓存上).

常用属性和 @Cacheable 差不多,这里不再赘述.

b)案例如下

例如实现缓存失效:现在要进行用户信息的修改,那么为了保证缓存和数据库中数据一致,修改完数据库之后的就直接删除对应的缓存数据即可~  下次查询时,再更新缓存.

这里通过 SpEL 表达式设置要删除的缓存的 key 为 动态的id + "userinfo" ,

    /***  通过 @CacheEvict 实现缓存失效,下次查询时,再更新缓存*/@CacheEvict(value = ["user"], key = "#dto.id + 'userinfo'")override fun updateUserinfo(dto: UserinfoDto) {//业务逻辑...println("修改数据库数据...")}

另外,还可以通过 属性,删除同一个分区下的所有缓存(慎用)

@CacheEvict(value = ["user"],  allEntries = true)

@CachePut

a)使用说明

@CachePut 用来更新缓存数据. 

与 @Cacheable 不同的是,使用 @CachePut 标注的方法在执行前不会检查缓存中是否存在这个数据,而是每次都会执行这个方法,并将返回值写入到缓存中.

属性上和 @Cacheable 是一样的,这里不再赘述.

b)案例如下

    @CachePut(value = ["user"], key = "#dto.id + 'userinfo'")override fun putUserinfo(dto: UserinfoDto): UserinfoVo {//业务逻辑println("更新数据库...")return with(dto) {UserinfoVo(id = id,name = name,age = age,)}}

@Caching

@Caching 用来组合以上多个操作.

例如删除同时删除多个缓存数据

    @Caching(evict = [CacheEvict(value = ["user"], key = "#dto.id + 'userinfo'"),CacheEvict(value = ["user"], key = "#dto.id + 1 + 'userinfo'"),])override fun updateUserinfo(dto: UserinfoDto) {//业务逻辑...println("修改数据库数据...")}

@CacheConfig

如果一个类中有很多一样的 cacheName、keyGenerator、cacheManager、cacheResolver,可以直接使用 @CacheConfig 在类上声明,那么这个类中的所有标记了 Cache 相关注解的方法都会共享 @CacheConfig 属性

@Service
//@CacheConfig(cacheNames = ["aaa", "bbb"]) 会创建两个缓存分区, aaa 和 bbb
@CacheConfig(cacheNames = ["user"])
class CacheServiceImpl: CacheService {@Cacheable(key = "#id + 'userinfo'")override fun getUserinfo(id: Long): UserinfoVo {//业务逻辑...println("查询数据库...")return UserinfoVo( // 这里的 UserinfoVo 必须要有无参构造才行,否则缓存将来读取的时候会报错id = id,name = "cyk",age = 21,)}/***  通过 @CacheEvict 实现缓存失效,下次查询时,再更新缓存*/@CacheEvict(key = "#dto.id + 'userinfo'")override fun updateUserinfo(dto: UserinfoDto) {//业务逻辑...println("修改数据库数据...")}@CachePut(key = "#dto.id + 'userinfo'")override fun putUserinfo(dto: UserinfoDto): UserinfoVo {//业务逻辑println("更新数据库...")return with(dto) {UserinfoVo(id = id,name = name,age = age,)}}}

SpringCache 的优势和劣势

读操作(优势)

SpringCache 在读操作上的处理的还是很到位的:

  • 缓存穿透:配置文件中设置 cache-null-values: true,这样就会将查询为 null 也缓存起来.
  • 缓存击穿:配置文件中设置 sync=true,这样就可以对方法进行加锁,解决击穿问题.
  • 缓存雪崩:配置文件中设置 time-to-live=3600000 用来设置过期时间(虽然设置的时间是统一的,但是一般情况下情况下触发的时机是不同的,也就相当于是有了随机因子).

写操作(劣势)

  • 对于读写并发高,或者写并发高的场景不太好应对.
  • 针对于一些特殊的写场景,还是要定制化一下的

总结

对于读多写少,一致性要求不高的数据,完全可以使用 SpringCache 来简化开发(只要缓存的数据有过期时间就可以).

对于一致性要求高的场景,也没必要引入引入缓存,直接对数据库进行读写即可.

特殊数据特殊处理.

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

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

相关文章

Flask 实现增改及分页查询的完整 Demo

Flask 实现增改及分页查询的完整 Demo 简介 本文将通过一个 Flask Demo 来展示如何使用 Flask 框架实现 RESTful API&#xff0c;包括增加、修改用户信息以及分页查询功能。我们将使用 Flask 的扩展 SQLAlchemy 来处理数据库操作。 环境准备 首先&#xff0c;确保你已安装 …

【Android面试题】请你分别采用递归和非递归对二叉树进行遍历?

请你分别采用递归和非递归对二叉树进行遍历? 这道题想考察什么? 1、二叉树的基本原理和遍历的方法? 考察的知识点 二叉树遍历的基本概念、二叉树的基本原理 考生如何回答 二叉树的基本概念 当然可以! 二叉树是一种常见的数据结构,它由一组称为节点的元素构成。每个…

地平线x3派开启core文件存储奔溃日志

开发环境 ubuntu22 ros humble c11 程序奔溃后core文件存储设置 1. 确保系统允许生成core文件 首先&#xff0c;检查和设置系统的core文件生成限制&#xff1a; 检查当前core文件大小限制 使用以下命令检查当前core文件大小限制&#xff1a; ulimit -c如果输出为 0&am…

reduce过滤递归符合条件的数据

图片展示 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head><…

Python 实现乘数加密法

乘数加密是简单代替密码的一种。乘数加密法脱胎于凯撒加密法,加密和解密符号设计把他们转换成数字,加上或者减去密钥,然后把新的数字转换回符号,当我们把加减密钥变成乘以密钥,就是乘法加密法。有关凯撒加密法可以看之前的文章《Python实现凯撒加解密》。 加密过程 乘数加…

力扣hot100:25. K 个一组翻转链表

LeetCode&#xff1a;25. K 个一组翻转链表 这个题很像24. 两两交换链表中的节点 和 206. 反转链表 的合并体。 在力扣hot100&#xff1a;24. 两两交换链表中的节点中我们使用递归来实现这个问题是很方便的&#xff0c;使用迭代在k个结点一组时就不太好使了&#xff0c;我们可…

【攻击绕过】IP速率限制绕过

【攻击绕过】IP速率限制绕过 1.探测对应功能的API接口2.在代码或参数中插入空白字符3.通过标头操纵IP来源4.修改其他标头5.利用代理网络6.利用 API 网关行为 1.探测对应功能的API接口 应尝试对目标功能的API接口执行暴力攻击&#xff0c;例如/api/v3/sign-up&#xff0c;包括/…

鸿蒙轻内核M核源码分析系列六 任务及任务调度(3)任务调度模块

调度&#xff0c;Schedule也称为Dispatch&#xff0c;是操作系统的一个重要模块&#xff0c;它负责选择系统要处理的下一个任务。调度模块需要协调处于就绪状态的任务对资源的竞争&#xff0c;按优先级策略从就绪队列中获取高优先级的任务&#xff0c;给予资源使用权。本文我们…

nc翻译公式注意事项

nc翻译公式注意事项 翻译3个及以上条件&#xff08;里面加引号的是表名和表中字段&#xff0c;没加引号的是VO中的字段&#xff0c;没加引号的不能是getcolvalue后的字段&#xff09; "pzh->getColValueMore(\"voucherref_view\",\"pk_voucher\"…

关于map并发读写问题的解决方案考虑小计

叠甲&#xff1a;未详细看底层实现&#xff0c;只是大概看了一下涉及的api的源码&#xff0c;理解错的地方勿喷 问题背景 公司业务中用到了一个map&#xff0c;作用是存储需要屏蔽的数据&#xff0c;请求打过来后会去其中匹配 若命中则直接退出 该map的使用有以下几个特点&am…

ffmpeg视频编码原理和实战-(4)H264原始码流分析

H.264是一种广泛使用的视频编码标准&#xff0c;它采用一种分层结构&#xff0c;其中最重要的一个层是NAL&#xff08;网络抽象层&#xff09;。在H.264编码中&#xff0c;原始码流&#xff08;bitstream&#xff09;是由多个NALU&#xff08;NAL Units&#xff09;组成的。了解…

欧洲历史的五个阶段

欧洲的历史基本上都是分裂的&#xff0c;大致可以分为五个时期&#xff0c;分别为古希腊时代、罗马帝国时代、中世纪时代&#xff0c;文艺复兴时代、工业革命时代。 一&#xff0c;古希腊时代 古希腊是西方文明的源头&#xff0c;也是最重要和最直接的文明起源&#xff0c;首…

Python编程学习第一篇——Python零基础快速入门(五)—字典

上期我们学习了List结构&#xff0c;今天大家一起来学习字典。Python中的字典是一种无序的数据类型&#xff0c;用于存储键值对(key-value)。它是一种可变的数据结构&#xff0c;可以在其中添加、删除和修改元素。字典中的键必须是唯一的&#xff0c;而值可以重复。 下面我们以…

前端想学习后端需要下载那些东西

前端想学习后端需要下载那些东西 需要下载的软件如下&#xff1a; 1、JDK的下载2、Tomcat的下载 3、MySQL的下载4、Navicat的下载 5、Maven的下载6、IDEA的下载下载教程&#xff1a;点击→此处 进入

【学习笔记】java SPI机制

目录 实现&#xff1a;示例&#xff1a;注意事项&#xff1a; Java SPI (Service Provider Interface) 是 Java 提供的一套用来发现和加载第三方服务的机制。SPI 的主要目的是为了解耦框架与插件&#xff08;服务提供商&#xff09;之间的依赖关系&#xff0c;使得框架能够灵活…

nexus搭建npm前端项目的私服

一、为什么要搭建私库 节省外网带宽加速maven构建部署第三方构件&#xff08;特别是无法从公共仓库下载的构件&#xff09;提高稳定性&#xff08;内网部署&#xff0c;更少地依赖外网&#xff09;降低中央仓库的负荷 构件&#xff0c;好比我们的藏书&#xff0c;去书店或商城…

深度学习——TensorBoard的使用

官方文档torch.utils.tensorboard — PyTorch 2.3 documentation TensorBoard简介 TensorBoard是一个可视化工具&#xff0c;它可以用来展示网络图、张量的指标变化、张量的分布情况等。特别是在训练网络的时候&#xff0c;我们可以设置不同的参数&#xff08;比如&#xff1…

【kubernetes】探索k8s集群的pod控制器详解(Deployment、StatefulSet、DaemonSet、Job、CronJob)

目录 一、Pod控制器及其功用 二、pod控制器有多种类型 2.1ReplicaSet 2.1.1ReplicaSet主要三个组件组成 2.2Deployment 2.3DaemonSet 2.4StatefulSet 2.5Job 2.6Cronjob 三、Pod与控制器之间的关系 3.1Deployment 3.2SatefulSet 3.2.1StatefulSet三个组件 3.2.2为…

为什么4个二进制位表示1个16进制位

为什么4个二进制位表示1个16进制位呢&#xff1f;首先需要了解二进制和十六进制分别是怎么样的。 1.二进制&#xff08;Binary&#xff09; 二进制就是只有2个数字的意思&#xff0c;这两个数字分别是0和1。如果我现在只有一个二进制数&#xff0c;那么这个二进制数只可能有两…