当前位置: 首页 > news >正文

Spring Boot 缓存知识体系大纲

 

一、缓存基础认知:明确核心缓存工具及特性

 

缓存基础认知的核心是区分“本地缓存”与“分布式缓存”两类工具,明确各自特性及适用边界,为后续实战选型提供依据。

 

1. 本地缓存:Java进程内缓存,特点为速度快但不支持多机共享

 

• 核心工具:

 

◦ Caffeine:Java领域性能最优的本地缓存,分类树实战场景中作为两级缓存的L1缓存

 

◦ 核心特性:基于内存操作(速度快)、支持过期时间/容量上限配置、需手动管理(put/getIfPresent/invalidate方法)

 

◦ 其他工具对比:

 

◦ ConcurrentHashMap:Java原生本地缓存,仅支持基础存取,无过期/容量控制功能

 

◦ Guava Cache:Caffeine的前身,性能低于Caffeine,已逐步被替代

 

• 核心结论:本地缓存仅适用于“单机部署、高频读”场景,多机部署下会出现数据不一致问题,需配合分布式缓存使用。

 

2. 分布式缓存:多机共享缓存,特点为数据一致但依赖网络

 

• 核心工具:

 

◦ Redis:Spring Boot生态中最常用的分布式缓存,分类树实战场景中作为两级缓存的L2缓存

 

◦ Java操作方式:

 

1. StringRedisTemplate:简化版操作工具,默认String序列化(存储JSON字符串无乱码,适配简单存取场景)

 

2. RedisTemplate:通用版操作工具,需手动配置序列化(否则存储数据会出现乱码)

 

◦ Redisson:进阶分布式缓存工具,支持分布式锁、复杂数据结构操作,暂不适用于分类树基础缓存场景,仅作了解。

 

• 核心结论:分布式缓存适用于“多机部署、数据共享”场景,可解决本地缓存多机数据不一致问题,但因依赖网络请求,速度慢于本地缓存。

 

二、实战缓存策略:一级缓存与两级缓存的选型逻辑

 

实战缓存策略的核心是“平衡速度与一致性”,需根据业务场景(读写字频、部署架构、实时性要求)选择一级缓存或两级缓存,分类树场景完美适配两级缓存。

 

1. 一级缓存:单一缓存工具方案,实现简单但存在明显缺陷

 

一级缓存指仅使用“本地缓存”或“分布式缓存”中的一种,实现简单但无法同时满足“速度”与“一致性”需求,具体对比如下:

方案 核心优点 核心缺点 分类树场景适配性 

仅本地缓存(Caffeine) 内存操作,速度极快(微秒级) 多机部署下数据不一致(分类更新后部分机器仍为旧数据) 单机部署适配,多机部署不适配 

仅分布式缓存(Redis) 多机数据一致,无同步问题 依赖网络请求,速度慢(毫秒级);高频读场景下Redis压力大 可实现需求,但用户体验差(加载慢) 

 

• 核心结论:一级缓存仅适用于“单机+低频读”或“多机+实时性极高(如秒杀库存)”场景,不适用于分类树“多机、高频读、实时性要求低”的核心需求。

 

2. 两级缓存:本地缓存(L1)+ 分布式缓存(L2)组合方案,实战最优解

 

两级缓存通过“本地缓存保速度、分布式缓存保一致”的组合,解决一级缓存的缺陷,分类树场景中采用该方案,核心逻辑如下:

 

(1)核心执行流程(分类树实战代码逻辑)

 

1. 缓存查询:优先查询L1(Caffeine)→ 命中则直接返回;未命中则查询L2(Redis)

 

2. 缓存回写:L2(Redis)命中→将数据回写到L1(Caffeine)后返回;L2未命中则执行数据库查询

 

3. 双缓存写入:数据库查询完成→构建分类树数据→同时写入L1(Caffeine)和L2(Redis)

 

4. 双缓存清除:分类更新/删除时→同时清除L1(invalidate方法)和L2(delete方法)

 

(2)适用场景(分类树场景完全匹配)

 

• 读多写少:查询频次远高于更新频次(如分类树日均查询1万次,更新1次)

 

• 多机部署:项目基于微服务架构,多台服务器同时提供服务

 

• 实时性要求低:数据可接受短延迟(如分类更新后,10分钟内个别用户看到旧数据)

 

• 核心结论:两级缓存是“速度(L1)”与“一致性(L2)”的最优平衡方案,为分类树等“多机、高频读、低实时性”场景的首选策略。

 

三、Spring Boot 缓存实现方式:手动编码与注解简化的对比

 

Spring Boot缓存实现存在两种核心方式:手动编码(灵活可控)与注解简化(代码简洁),需根据场景复杂度选择,两级缓存适配手动编码,单级缓存适配注解简化。

 

1. 手动编码实现:灵活可控,适配两级缓存场景

 

手动编码指通过代码逻辑控制“缓存查询、数据库查询、缓存写入/清除”的全流程,分类树初始实战代码采用该方式,核心特点为灵活,支持自定义两级缓存逻辑。

 

(1)核心代码示例(分类树场景)

public List<Category> getCategoryTree() {

    // 1. 查询L1缓存(Caffeine)

    List<Category> result = localCache.getIfPresent(CACHE_KEY);

    if (result != null) return result;

    

    // 2. 查询L2缓存(StringRedisTemplate)

    String json = stringRedisTemplate.opsForValue().get(CACHE_KEY);

    if (StringUtils.hasText(json)) {

        result = JSON.parseArray(json, Category.class);

        localCache.put(CACHE_KEY, result); // 回写L1缓存

        return result;

    }

    

    // 3. 数据库查询(构建分类树)

    result = categoryMapper.selectAllForTree();

    result = treeBuilder.buildTree(result, null);

    

    // 4. 双缓存写入

    localCache.put(CACHE_KEY, result);

    stringRedisTemplate.opsForValue().set(CACHE_KEY, JSON.toJSONString(result), 2, TimeUnit.HOURS);

    

    return result;

}

(2)核心优缺点

 

• 优点:逻辑灵活,可自由实现两级缓存、自定义缓存回写/清除规则

 

• 缺点:代码冗余,需编写大量if-else判断逻辑,重复代码多

 

2. 注解简化实现:代码简洁,适配单级缓存场景

 

注解简化指通过Spring Boot提供的缓存注解,替代手动编码的if-else逻辑,核心为3个注解,仅适用于单级缓存(如仅Redis),两级缓存场景需自定义管理器(复杂度高)。

 

(1)核心注解及作用(对应手动编码逻辑)

注解 核心作用(替代手动编码逻辑) 分类树场景用法 

@Cacheable 自动执行“缓存查询→命中返回→未命中查库→写入缓存”流程 标注于getCategoryTree()方法,替代“查询+写入”逻辑 

@CacheEvict 自动执行缓存清除操作(更新/删除分类时) 标注于updateCategory()方法,替代手动双缓存清除逻辑 

@CachePut 自动执行“查库→更新缓存”流程(新增分类时) 标注于addCategory()方法,替代“新增查库+缓存写入”逻辑 

 

(2)关键前提:必须配置RedisCacheManager

 

注解简化实现的核心前提是配置RedisCacheManager,解决两大问题:

 

1. 避免注解默认使用本地缓存(ConcurrentHashMap),强制指定使用Redis

 

2. 解决Redis存储数据乱码问题,配置String/JSON序列化规则

 

(3)核心优缺点

 

• 优点:代码简洁,无冗余if-else逻辑,开发效率高

 

• 缺点:灵活性低,难以实现两级缓存逻辑;需额外配置RedisCacheManager

 

四、RedisCacheManager:注解简化实现的核心配置

 

RedisCacheManager是注解简化实现的核心,作用为“给缓存注解设定执行规则”,解决注解默认使用本地缓存、存储数据乱码两大缺陷,配置逻辑需结合业务场景(如分类树过期时间)。

 

1. 核心作用

 

• 规则1:指定缓存介质为Redis,替代默认的本地缓存(ConcurrentHashMap),保证多机数据一致

 

• 规则2:配置序列化规则,实现“key String格式、value JSON格式”存储,避免Redis数据乱码

 

• 规则3:自定义缓存过期时间,支持“默认规则+场景化规则”(如分类树缓存2小时过期)

 

2. 核心配置代码(分类树场景适配)

@Configuration

@EnableCaching // 开启Spring Boot缓存注解功能,必须添加

public class RedisCacheConfig {

 

    @Bean

    public RedisCacheManager redisCacheManager(RedisConnectionFactory factory) {

        // 1. 配置序列化规则(key String,value JSON,避免乱码)

        RedisSerializer<String> keySerializer = new StringRedisSerializer();

        Jackson2JsonRedisSerializer<Object> valueSerializer = new Jackson2JsonRedisSerializer<>(Object.class);

        ObjectMapper objectMapper = new ObjectMapper();

        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);

        valueSerializer.setObjectMapper(objectMapper);

 

        // 2. 配置默认缓存规则(所有缓存通用)

        RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()

                .entryTtl(Duration.ofMinutes(30)) // 默认过期时间:30分钟

                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer))

                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer))

                .disableCachingNullValues(); // 禁止缓存null值,避免缓存穿透

 

        // 3. 配置分类树缓存自定义规则(场景化适配)

        Map<String, RedisCacheConfiguration> customConfigMap = new HashMap<>();

        // 分类树缓存(注解中value="categoryCache")过期时间:2小时

        customConfigMap.put("categoryCache", defaultConfig.entryTtl(Duration.ofHours(2)));

 

        // 4. 生成RedisCacheManager,绑定规则

        return RedisCacheManager.builder(factory)

                .cacheDefaults(defaultConfig) // 应用默认规则

                .withInitialCacheConfigurations(customConfigMap) // 应用分类树自定义规则

                .build();

    }

}

3. 核心配置点(新手必关注)

 

• 序列化配置:必须使用StringRedisSerializer(key)+Jackson2JsonRedisSerializer(value),否则Redis存储数据乱码

 

• 过期时间配置:区分“默认规则”(30分钟)与“场景化规则”(分类树2小时),根据业务高频次调整

 

• 空值缓存控制:启用disableCachingNullValues(),禁止缓存null值,避免缓存穿透问题

 

五、缓存进阶优化方案:解决实战中的核心问题

 

缓存进阶优化的核心是“保证缓存稳定运行”,针对缓存一致性、缓存三大经典问题(穿透/击穿/雪崩),提供分类树场景可直接复用的解决方案。

 

1. 缓存一致性保障:避免数据更新后缓存与数据库不一致

 

缓存一致性的核心原则是“先更新数据库,再清除缓存”,顺序不可颠倒,批量操作需使用组合注解。

 

(1)核心原则:更新/删除数据的执行顺序

 

• 错误顺序:先清除缓存→再更新数据库 → 极端场景下导致数据不一致(线程A清缓存→线程B查库写旧数据→线程A更库)

 

• 正确顺序:先更新数据库→再清除缓存 → 分类树实战代码采用该顺序,保证数据一致性

 

(2)批量操作处理:使用@Caching组合注解

 

批量操作(如批量删除分类)需同时清除多个缓存时,使用@Caching组合注解,替代多次单独标注@CacheEvict。

// 批量删除分类,同时清除“分类树缓存”与“单个分类缓存”

@Caching(evict = {

    @CacheEvict(value = "categoryCache", key = "'category:tree:all'"), // 清除分类树缓存

    @CacheEvict(value = "categoryCache", key = "'category:single:'+#ids") // 清除单个分类缓存

})

public void batchDeleteCategory(List<Long> ids) {

    categoryMapper.deleteBatchIds(ids);

}

2. 缓存三大经典问题:高并发场景下的解决方案

 

缓存三大经典问题(穿透/击穿/雪崩)是高频读场景(如分类树)的常见风险,需针对性配置解决方案,直接复用至实战代码。

问题类型 分类树场景表现 解决方案(可直接复用至代码) 

缓存穿透 频繁查询不存在的分类(如id=-1),每次请求穿透至数据库 数据库查询返回null时,手动写入“空值”至缓存,设置5分钟短过期:if (result == null) localCache.put("category:single:-1", null, 5, TimeUnit.MINUTES); 

缓存击穿 分类树key(热点key)过期瞬间,大量请求同时穿透至数据库 给热点key添加随机过期时间,避免同时过期:.expireAfterWrite(10 + new Random().nextInt(5), TimeUnit.MINUTES)(10-15分钟随机) 

缓存雪崩 大量分类缓存key同时过期,导致数据库请求量骤增 1. 所有缓存key添加随机过期时间;2. Redis部署集群/哨兵模式,避免Redis单点故障 

 

六、工具选型决策依据:不同场景的最优缓存方案

 

工具选型需基于“业务场景需求”,结合缓存工具特性,选择最优方案,分类树场景完全适配“Caffeine(L1)+ StringRedisTemplate(L2)”组合。

场景需求 最优缓存工具/方案 分类树场景适配度 

单机部署+高频读+简单缓存 Caffeine(本地缓存) 不适配(分类树为多机部署) 

多机部署+简单String存取 StringRedisTemplate(手动编码实现) 适配(作为L2缓存) 

多机部署+注解简化+单级缓存 @Cacheable + RedisCacheManager 部分适配(可简化L2缓存代码) 

多机部署+高频读+两级缓存 Caffeine(L1)+ StringRedisTemplate(L2) 完全适配(当前实战方案) 

多机部署+复杂场景(分布式锁) Redisson 暂不适配(无复杂场景需求) 

 

核心总结

 

Spring Boot缓存知识体系围绕“工具认知→策略选型→实现方式→优化方案→选型决策”层层递进,核心逻辑为:

 

1. 基础层:区分本地缓存(Caffeine)与分布式缓存(Redis)的特性边界

 

2. 策略层:“多机、高频读、低实时性”场景优先选择两级缓存

 

3. 实现层:两级缓存用手动编码,单级缓存用注解简化(需配RedisCacheManager)

 

4. 优化层:遵循“先更库再清缓存”保障一致性,针对性解决三大经典问题

 

5. 选型层:基于场景需求选择工具,拒绝“一刀切”方案

http://www.dtcms.com/a/523493.html

相关文章:

  • 破局政务数字化核心难题:金仓数据库以国产化方案引领电子证照系统升级之路
  • XML:从基础到 Schema 约束的全方位解析
  • 技术引领场景革新|合合信息PRCV论坛聚焦多模态文本智能前沿实践
  • 海南网站建设网络货运平台有哪些
  • 系统架构设计师备考第53天——业务逻辑层设计
  • 科技创新与数字化制造转型在“十五五”规划中的意义
  • 网站开发最新技术wordpress4.7.4密码
  • HarmonyOS方舟编译器与运行时优化
  • HarmonyOS AI能力集成与端侧推理实战
  • 自己做公众号和小说网站推广济南网站建设艮安
  • 阿里云国际站GPU:阿里云GPU的应用场景有哪些?
  • 【工具】Scrcpy|安卓投屏电脑的开源工具Scrcpy的安装及看电视注意事项
  • penCV轻松入门_面向python(第七章 图像平滑处理)
  • html5移动网站开发流程各类设计型网站
  • 使用C#代码在Excel中创建数据透视表
  • 反爬克星还是效率神器?Browser-Use+cpolar重构Web自动化逻辑
  • 《KingbaseES数据库:首个多院区异构多活容灾架构,浙人医创新开新篇》
  • MySQL 的 MyISAM 与 InnoDB 存储引擎的核心区别
  • 【Qt开发】容器类控件(一)-> QGroupBox
  • 生活电器:重构家居体验的产业变革与发展探索
  • 怎么在百度建立自己的网站58同城西安网站建设
  • Modbus笔记
  • JVM 分代收集算法(Generational GC) 的原理和执行流程
  • 网站名加引号wordpress+极简博客
  • 网站备案取消接入什么是网站反链
  • 淘宝客网站需要多大主机分销网站怎么做
  • 荣县规划和建设局网站wordpress换网址
  • 广州市企业网站建设怎么样合肥做公司网站
  • 如何建设视频资源电影网站贵港网站建设兼职
  • 山东省建设项目备案证明网站怎么修复网站死链