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: redis
    redis:
      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 {

    @Bean
    fun 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 来简化开发(只要缓存的数据有过期时间就可以).

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

特殊数据特殊处理.

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【网络协议 | HTTP】HTTP总结与全梳理(一) —— HTTP协议超详细教程

&#x1f525;博客简介&#xff1a;开了几个专栏&#xff0c;针对 Linux 和 rtos 系统&#xff0c;嵌入式开发和音视频开发&#xff0c;结合多年工作经验&#xff0c;跟大家分享交流嵌入式软硬件技术、音视频技术的干货。   ✍️系列专栏&#xff1a;C/C、Linux、rtos、嵌入式…

nginx代理vue项目路由跳转刷新

常规代理 在我们日常开发中&#xff0c;前端部署到服务器&#xff0c;需要用到nginx部署&#xff0c;简单代理如下&#xff1a; #user nobody; worker_processes 1;#error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info;#pid…

GPT革命:AI如何重塑我们的未来!

GPT革命&#xff1a;AI如何重塑我们的未来&#xff01; &#x1f604;生命不息&#xff0c;写作不止 &#x1f525; 继续踏上学习之路&#xff0c;学之分享笔记 &#x1f44a; 总有一天我也能像各位大佬一样 &#x1f3c6; 博客首页 怒放吧德德 To记录领地 &#x1f31d;分享…

并查集算法

目录 1.算法介绍 1.1什么是并查集呢&#xff0c;它又是用来干什么的呢&#xff1f; 1.2问题引入 2.算法解析 2.1初始化 2.2合并操作 2.3查找 路径压缩 2.4问题解决代码 3.变式突破 1.算法介绍 1.1什么是并查集呢&#xff0c;它又是用来干什么的呢&#xff1f; 逐字拆…

经典的泡泡龙游戏源码免费下载

源码介绍 HTML5泡泡龙冒险小游戏是一款休闲网页游戏&#xff0c;游戏玩法是玩家从下方中央的弹珠发射台射出彩珠&#xff0c;多于3个同色珠相连则会消失。 源码下载 经典的泡泡龙游戏源码免费下载

【Python绘画】画正方形简笔画

本文收录于 《一起学Python趣味编程》专栏&#xff0c;从零基础开始&#xff0c;分享一些Python编程知识&#xff0c;欢迎关注&#xff0c;谢谢&#xff01; 文章目录 一、前言二、代码示例三、知识点梳理四、总结 一、前言 本文介绍如何使用Python的海龟画图工具turtle&#…

docker-compose入门级实战教程

&#x1f31f;&#x1f30c; 欢迎来到知识与创意的殿堂 — 远见阁小民的世界&#xff01;&#x1f680; &#x1f31f;&#x1f9ed; 在这里&#xff0c;我们一起探索技术的奥秘&#xff0c;一起在知识的海洋中遨游。 &#x1f31f;&#x1f9ed; 在这里&#xff0c;每个错误都…

Flutter Bloc之简单记录

目录 0.库安装 1.插件和自动生成 2.状态的配置 1.初始化中&#xff1a; 2.赋值完成后&#xff1a; 3.如果出错&#xff1a; 3.事件的配置 1.定义一个读取事件 2.定义一个更改事件 4.Bloc的设置 5.Bloc的使用 1.BlocProvider 2.内部调用 参考文章进行类的配置 0.库…

【ARM Cache 系列文章 2.1 -- Cache PoP 及 PoDP 介绍】

请阅读【ARM Cache 及 MMU/MPU 系列文章专栏导读】 及【嵌入式开发学习必备专栏】 文章目录 PoP 及 PoDPCache PoDPCache PoP应用和影响PoP 及 PoDP Cache PoDP 点对深度持久性(Point of Deep Persistence, PoDP)是内存系统中的一个点,在该点达到的任何写操作即使在系统供电…

跳跃游戏二

方法一&#xff1a;&#xff08;双指针法&#xff09;此题参考跳台阶问题&#xff0c;题目要求求到达最后一个点的最小跳跃次数&#xff0c;那么我们就可以从最后一个往前推&#xff0c;先看谁能离得最远&#xff0c;并且能跳到最后一个。假设i位置是离最后一个位置最远&#x…

网工内推 | 联通公司,云计算售前,AWS认证优先

01 联通数字科技有限公司 &#x1f537;招聘岗位&#xff1a;云计算售前工程师 &#x1f537;职责描述&#xff1a; 1.了解私有云&#xff0c;公有云&#xff0c;混合云等云计算技术知识&#xff0c;了解云计算行业现状及发展趋势。 2.承担区域项目售前工作支持&#xff0c;为…

Linux基础指令磁盘管理002

LVM&#xff08;Logical Volume Manager&#xff09;是Linux系统中一种灵活的磁盘管理和存储解决方案&#xff0c;它允许用户在物理卷&#xff08;Physical Volumes, PV&#xff09;上创建卷组&#xff08;Volume Groups, VG&#xff09;&#xff0c;然后在卷组上创建逻辑卷&am…

NSS题目练习7

[MoeCTF 2022]baby_file 打开看见一串源代码&#xff0c;需要get传参传入file 题目提示php伪协议 用dirsearch扫描发现flag.php 用php伪协议查看&#xff0c;回显一串base64编码 解码后得到flag [鹤城杯 2021]Middle magic 读取这两个文件 一个php正则表达式 补充&#xff1a…

Element ui图片上传

前言 对于广大小白来说&#xff0c;图片上传简直是上传难&#xff0c;难于上青天&#xff01;废话不多说&#xff0c;步入正题&#xff0c;您就瞧好吧&#xff01; 步骤一&#xff1a;前端使用element ui组件&#xff08;upload上传&#xff09; 我个人喜欢使用第二个组件&a…

Python 实现乘数加密法

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

【宠粉赠书】大模型时代的网络安全:安恒“网安三剑客”实战指南

不知不觉中&#xff0c;小智的粉丝已经突破一万。为了回馈粉丝们的厚爱&#xff0c;今天小智给大家送上一套网络安全界的三宝书——安恒"网安三剑客"。下面我会详细给大家介绍这套图书&#xff0c;文末留有领取方式。 随着人工智能&#xff08;AI&#xff09;和大模型…

HarmonyOS(二十五)——Harmonyos通用事件之点击事件

组件被点击时触发的事件就是点击事件。 1.事件 名称支持冒泡功能描述onClick(event: (event?: ClickEvent) > void)否点击动作触发该回调&#xff0c;event返回值见ClickEvent对象说明。从API version 9开始&#xff0c;该接口支持在ArkTS卡片中使用。 2.ClickEvent对象…

Etcd Raft架构设计和源码剖析1:宏观架构

Etcd Raft架构设计和源码剖析1&#xff1a;宏观架构 | Go语言充电站 序言 Etcd提供了一个样例contrib/raftexample&#xff0c;用来展示如何使用etcd raft。这篇文章通过raftexample介绍如何使用etcd raft。 raft服务 raftexample是一个分布式KV数据库&#xff0c;客户端可…

MySQL数据库常见工具的基础使用_1

在上一篇文章中提到了对MySQL数据库进行操作的一些常见工具 mysqlcheck mysqlcheck是一个用于数据库表的检查&#xff0c;修复&#xff0c;分析和优化的一个客户端程序 分析的作用是查看表的关键字分布,能够让sql生成正确的执行计划(支持InnoDB,MyISAM,NDB)检查的作用是检查…

BGP基础配置实验

接下来R1&#xff0c;R2就可以通过三次握手建立TCP会话 然后建立R2&#xff0c;R4邻居 对R5和R4 然后拿4ping5 但是在4&#xff0c;5之间不能跑别的协议&#xff0c;然后要直接告知才可以 再ping通了建立邻居 用环回建邻居要改一下原 然后在建立的时候要把R4TTL值改成2&#xf…