惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tailwind CSS Blog
博客园 - Franky
博客园 - 司徒正美
Stack Overflow Blog
Stack Overflow Blog
GbyAI
GbyAI
Y
Y Combinator Blog
Microsoft Azure Blog
Microsoft Azure Blog
Blog — PlanetScale
Blog — PlanetScale
博客园 - 叶小钗
罗磊的独立博客
The GitHub Blog
The GitHub Blog
H
Help Net Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Engineering at Meta
Engineering at Meta
Martin Fowler
Martin Fowler
Spread Privacy
Spread Privacy
Google DeepMind News
Google DeepMind News
AWS News Blog
AWS News Blog
N
Netflix TechBlog - Medium
T
The Exploit Database - CXSecurity.com
云风的 BLOG
云风的 BLOG
小众软件
小众软件
aimingoo的专栏
aimingoo的专栏
Cyberwarzone
Cyberwarzone
T
Threatpost
T
Threat Research - Cisco Blogs
Recent Announcements
Recent Announcements
T
Tor Project blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Last Week in AI
Last Week in AI
L
Lohrmann on Cybersecurity
量子位
V
V2EX
V
Vulnerabilities – Threatpost
L
LINUX DO - 热门话题
www.infosecurity-magazine.com
www.infosecurity-magazine.com
P
Proofpoint News Feed
I
Intezer
Schneier on Security
Schneier on Security
雷峰网
雷峰网
B
Blog RSS Feed
Jina AI
Jina AI
爱范儿
爱范儿
Attack and Defense Labs
Attack and Defense Labs
Google DeepMind News
Google DeepMind News
M
MIT News - Artificial intelligence
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Palo Alto Networks Blog
美团技术团队
博客园 - 【当耐特】

XINDOO

关于内卷,几个值得深想的洞察 当创作被 Skill 化:我用 AI 写了一部 320 章的长篇网文 AI第一剑,先斩程序员 AI 也会偷懒?这个 PUA 工具专治“摸鱼式编程” Gstack 深度解析:YC CEO 开源的 AI 工程团队 GitHub Trending霸榜!深度解析AI Coding辅助神器 Superpowers 深度探讨:从 OpenClaw 爆火,看 AI Agent 的真相与程序员的未来 我复刻了NotebookLM的信息图功能 我用AI写了部小说,这里是整个过程 [翻译]我在谷歌14年学到的21堂课 2万字吊打40万字:为什么我的“牛马Agent”比“数字分身”更聪明? 最近AI领域爆火的 Agent Skills 是什么? 从计算机科学的视角来看拖延症 一周改6个库后我悟了:AI时代,程序员正在退化成“甲方” 使用n8n做一个自动同步更新的Github项目问答机器人 Agent设计模式——第 20 章:优先级排序 Agent设计模式——第 1 章:提示词链 Agent设计模式——附录 D - 使用 AgentSpace 构建 Agent Agent设计模式——第 19 章:评估和监控 Agent设计模式——第 10 章:模型上下文协议 (MCP) Agent设计模式——第 21 章:探索和发现 Agent设计模式——智能体设计模式 Agent设计模式——第 11 章:目标设定和监控 Agent设计模式——第 9 章:学习和适应 Agent设计模式——第 16 章:资源感知优化 Agent设计模式——附录 G - 编码 Agent Agent设计模式——第 13 章:人机协同 Agent设计模式——第 17 章:推理技术 Agent设计模式——附录 F - 深入剖析:Agent 推理引擎的内部运作机制 Agent设计模式——章节目录 Agent设计模式—— Agent设计模式——第 7 章:多 Agent 协作 Agent设计模式——附录 E - 命令行界面中的 AI Agent Agent设计模式——附录 C - Agentic 框架快速概览 Agent设计模式——第 3 章:并行化 Agent设计模式——**常见问题解答:Agentic 设计模式** Agent设计模式——第 14 章:知识检索(RAG) Agent设计模式——第 18 章:Guardrails/安全模式 Agent设计模式——第 15 章:Agent 间通信(A2A) Agent设计模式——第 8 章:内存管理 Agent设计模式——第 12 章:异常处理和恢复 Agent设计模式——第 4 章:反思 Agent设计模式——附录 B - AI Agentic 交互:从图形界面到现实世界环境 Agent设计模式——第 5 章:工具使用(函数调用) Agent设计模式——结论 Agent设计模式——第 6 章:规划 Agent设计模式——第 2 章:路由 从经验主义到贝叶斯理论:如何排查线上问题 我用AI为自己造了一把安全的开发者“瑞士军刀”” 从LLM和MCP的协同过程看如何做优化 打通Dify与AI工具生态:将Workflow转为MCP工具的实践 一文了解知识库背后的技术RAG AI应用的五个级别:从入门到专家的进阶之路 一文入门AI圈最近爆火的MCP协议 HTTP/3:性能改进(第 2 部分) deepseek-r1祛魅:从过度热捧到理性认知⁠ 为什么AI智能体需要工作流 如何用GPT-4o解读视频 json命令行处理神器jq介绍 OpenAI的结构化浅析 从大模型的原理到提示词优化 从经济学原理看团队分工合作 [翻译]关于人工智能的30个思考 从马斯洛需求层次理论谈职场激励 知识与智慧 如何使用大语言模型绘制专业图表 两个开源项目打造自己的大模型聚合平台 我让gpt4o给我推荐了一千多次书 得到了这些数据 用Langchain创建一个可以总结网页内容的Agent 推荐一个好用的命令行工具ShellGPT 关于ffmpeg height not divisible by 2的错误 使用Certbot解决https证书自动更新的问题 软件开发中的抓大放小vs极致细节思维 OpenAI Assistants-API简明教程 OpenAI的多函数调用(Multiple Function Calling)简介 如何使用ffmpeg制作透明背景的视频 spring-kafka中ContainerProperties.AckMode详解 如何在地图上寻找最密集点的位置? IO密集型服务提升性能的三种方法 职场中的基本归因错误和自利归因 使用javax.validation.constraints校验参数合法性 Java Optional:让你的代码更优雅 ChatGPT函数调用初体验:让ChatGPT具备抓取网页文本的能力 如何使用ChatGPT提升自己的“码”力? 使用ffmpeg拼接两张图片 ThreadPoolExecutor——高效处理并发任务的必备良器 从CPU的视角看 多线程代码为什么那么难写! 使用ffmpeg缩小视频体积的几种方式 Linux parallel 命令使用手册 为什么说过早优化是万恶之源? Linux xargs命令介绍 深入理解Spring的事件通知机制 Java高并发之CyclicBarrier简介 聊一聊过度设计! 详解Redisson分布式限流的实现原理 Java中使用HashMap时指定初始化容量性能一定会更好吗? 如何用ffmpeg截取视频片段&截取时间不准确的坑 XINDOO的2022年年终总结 使用ffmpeg将视频转成HLS(m3u8)格式 谷歌Guava LoadingCache介绍
Spring Cache简明教程
xindoo · 2024-03-17 · via XINDOO

@[TOC](Spring Cache简明教程)

引言

  在我上一篇文章如何正确使用缓存来提升系统性能中,我从偏理论的视角介绍了Cache在性能优化中的必要性,在这篇文章中我们介绍Spring全家桶中和cache相关Spring-Cache。

什么是Spring Cache?

  Spring Cache是Spring框架提供的一个抽象层,专注于提供一种透明的方式来添加缓存功能到Spring应用程序中。它不是一个具体的缓存实现,而是提供了一套创建和管理缓存的标准,并能够与多种缓存实现无缝集成,例如Ehcache、Caffeine、Redisson等。

核心特性

  以下是Spring Cache的一些核心特性:

  • 声明式缓存抽象:通过Java注解,开发者可以声明性地控制方法的缓存行为,而无需编写具体的缓存逻辑。
  • 无需改变代码结构:缓存逻辑通过AOP增强被注解的方法,因此不需要修改方法的实际代码。
  • 支持多种缓存库:与多个流行的缓存库兼容,开发者可以根据自身需求选择最适合的缓存解决方案。
  • 灵活的缓存配置:可以通过配置文件灵活地管理缓存行为,包括缓存的名称、过期时间和条件等。
  • 动态缓存决策:支持在运行时根据方法执行的上下文动态地做出缓存决策。

应用场景

  Spring Cache适用于以下应用场景:

  • 提高性能:对于那些计算成本高昂或者频繁访问的数据,通过缓存可以显著提高系统的响应速度。
  • 减少数据库压力:缓存可以减少数据库的读操作,对于读多写少的场景特别有用。
  • 提高系统可扩展性:通过使用分布式缓存,可以在不增加数据库负荷的情况下,横向扩展应用程序。

如何工作

  Spring Cache背后的工作原理基于Spring AOP(面向切面编程),它会在运行时动态地创建代理对象,来拦截对被注解方法的调用。根据注解的不同,Spring Cache可以执行如下操作:

  • @Cacheable:在方法执行前先检查缓存,如果缓存中已经存在相应的数据,则直接返回缓存数据而不执行方法。
  • @CachePut:无论如何都会执行方法,并将执行结果放入指定的缓存中。
  • @CacheEvict:删除缓存中的数据,通常用于删除操作或数据更新后的缓存同步。
  • @Caching:组合多个缓存操作,可以同时使用以上几种注解。

通过上述机制,Spring Cache提供了一个简单而强大的缓存管理能力,使得开发者能够专注于业务逻辑的实现,而将缓存的维护交给框架去处理。

如何使用

1. 添加依赖

  我们拿SpringBoot Maven的项目为例,说下如何在项目中使用Spring Cache,首先很简单,需要在pom文件中引入Spring Cache相关的依赖。

<dependencies>
    <!-- 添加Spring Boot Cache Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>

    <!-- 如果使用特定的缓存实现,如Caffeine -->
    <dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
    </dependency>
</dependencies>

2. 启用缓存

  另外还需要在Spring Boot应用程序的主类或任何配置类上使用@EnableCaching注解来启用缓存支持。

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableCaching
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

3. 配置缓存

  虽然Spring Boot为许多缓存实现提供了自动配置,但你也可以通过application.properties或application.yml文件进行自定义配置。例如,如果你使用Caffeine作为缓存实现,可以按以下方式配置:

# application.properties
spring.cache.cache-names=cache1,cache2
spring.cache.caffeine.spec=maximumSize=500,expireAfterAccess=600s

当然你也可以通过代码的形式定义CacheManager,实现对Cache的配置,代码如下:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCacheManager;

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager("cache1", "cache2");
        cacheManager.setCaffeine(Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES));
        return cacheManager;
    }
}

4. 使用缓存注解

  在服务中,你可以通过在方法上添加相应的缓存注解来实现缓存逻辑。

  • 使用@Cacheable来缓存方法的返回结果。
import org.springframework.cache.annotation.Cacheable;

public class SomeService {

    @Cacheable("cache1")
    public SomeObject getSomeObject(String id) {
        // 方法实现,如果缓存中有对应id的对象,则不执行此代码
        return fetchFromDatabase(id);
    }
}
  • 使用@CachePut来更新缓存。
import org.springframework.cache.annotation.CachePut;

public class SomeService {

    @CachePut(value = "cache1", key = "#someObject.id")
    public SomeObject updateSomeObject(SomeObject someObject) {
        // 方法实现,总是执行并刷新缓存
        return saveToDatabase(someObject);
    }
}
  • 使用@CacheEvict来清除缓存。
import org.springframework.cache.annotation.CacheEvict;

public class SomeService {

    @CacheEvict(value = "cache1", key = "#id")
    public void deleteSomeObject(String id) {
        // 方法实现,删除对象的同时清除缓存
        removeFromDatabase(id);
    }
}

你还可以使用@Caching来组合多个缓存操作。

使用缓存的注意事项

  使用Spring Cache时,需要注意以下几个关键点:

缓存的数据序列化

  当使用分布式缓存或需要将缓存数据存储在磁盘上时,数据序列化变得非常重要。你需要确保你的对象可以被序列化和反序列化,否则会抛出异常。对于复杂对象,考虑使用JSON或其他自定义序列化策略,当你不指定序列化策略时,默认会使用java序列化,这时候就要求你必须实现Serializable接口。

缓存键的生成

  默认情况下,Spring Cache使用方法参数的hashCode()equals()方法来生成缓存键。如果你的方法参数是自定义的对象,确保这些方法被适当地覆盖。你也可以通过实现KeyGenerator接口或使用key属性自定义键的生成。

缓存内容的一致性

  缓存数据可能会与数据库中的数据不一致。当数据被更新或删除时,你需要使用@CachePut@CacheEvict注解来确保缓存与数据源保持同步。

缓存的并发问题

  虽然缓存操作通常是原子性的,但在高并发环境下仍然可能遇到并发问题。例如,多个线程可能同时计算同一个缓存缺失的值。为了避免这种情况,你可能需要使用锁或其他同步机制。

缓存穿透

  缓存穿透是指查询不存在的数据。因为缓存不会存储这样的数据,所以每次查询都会打到后端数据库,从而可能造成数据库的压力。为了预防这种情况,可以采用布隆过滤器或者将查询结果为空的情况也缓存起来。

缓存雪崩

  缓存雪崩指在缓存失效后,大量的请求同时到达数据库,可能会导致数据库瞬时压力过大。为了防止这种情况,可以设置不同的缓存过期时间,使用缓存预热策略,或者实施熔断限流措施。

缓存的存储容量

  对于本地缓存,缓存的大小应当根据可用内存合理配置,避免内存溢出。对于分布式缓存,应当考虑其存储容量和扩展性。

方法的可见性和返回类型

  @Cacheable本身逻辑也是基于SpringAOP实现的,所以需要和其他缓存注解一样应用于公共方法。对私有方法、final方法或类、static方法使用缓存注解是无效的,因为Spring的AOP无法拦截这些方法的调用。同样,缓存方法的返回类型应该是非null值,因为大多数缓存实现都不会存储null值。如果方法可能返回null,那么需要进行额外的处理来避免缓存穿透。

事务性操作和缓存

  如果在事务性操作中使用缓存,需要注意事务的传播行为和缓存操作的顺序。错误的操作顺序可能会导致缓存与数据库状态不一致。

总结

  本文详细介绍了Spring Cache的使用和注意事项。Spring Cache作为Spring框架提供的缓存抽象,允许通过声明式注解轻松地在应用中集成缓存,以此提升性能和减少开发时间。以下是本文关键点的总结:

  1. Spring Cache不是缓存实现:它提供了一组与缓存实现无关的接口和注解。
  2. 简单的集成步骤:包括添加依赖、启用缓存、配置缓存以及在方法上使用缓存注解。
  3. 缓存注解的使用:介绍了@Cacheable@CachePut@CacheEvict等注解的使用场景。
  4. 注意事项
    • 数据序列化:确保对象可以被序列化和反序列化。
    • 缓存键生成:覆盖hashCode()equals()或自定义键的生成。
    • 缓存内容一致性:使用注解确保缓存与数据源同步。
    • 并发问题:可能需要锁或其他同步机制。
    • 缓存穿透:使用布隆过滤器或缓存空查询。
    • 缓存雪崩:设置不同的缓存过期时间,缓存预热策略,或实施熔断限流。
    • 缓存容量:合理配置本地缓存大小,考虑分布式缓存的存储容量和扩展性。
    • 方法的可见性:缓存注解应用于公共方法。
    • 事务性操作:注意事务的传播行为和缓存操作的顺序。

备注:本文使用了部分AI生成内容,作者已对有效性做了校验,请放心查阅。