springcache缓存

缓存

本地缓存:一种将数据存储在应用程序本地内存中的技术,目的是减少对后端数据源(如数据库、远程服务)的访问次数,从而提高应用程序的性能和响应速度。本地缓存通常用于存储频繁访问且不经常变化的数据。

本地缓存的优势

  1. 提高性能:减少对后端数据源的访问次数,降低网络延迟,提高响应速度。
  2. 减轻后端压力:减少后端数据源的负载,提高系统的整体性能。
  3. 提高可用性:即使后端数据源不可用,缓存中的数据仍然可以提供服务。

本地缓存的局限性

  1. 数据一致性:缓存中的数据可能与后端数据源中的数据不一致,需要有效的缓存更新机制。
  2. 内存限制:本地缓存占用内存资源,需要合理管理缓存大小。
  3. 单点故障:本地缓存依赖于单个节点,如果节点故障,缓存中的数据会丢失

@EnableCaching:开启缓存注解功能,通常加在启动类上

@Cacheable:在方法执行前先查询缓存中是否有数据,如果有数据,则直接返回缓存数据,如果没有缓存数据,调用方法将方法返回值放到缓存中

@CachePut:将方法的返回值放到缓存

@CacheEvict:将一条或多条数据从缓存中删除

xml 复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

开启缓存功能

java 复制代码
package com.itheima;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootConfiguration
@EnableCaching      // 开启缓存功能
public class SpringbootApplication {

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

}

使用缓存

java 复制代码
package com.itheima.service.impl;

import com.itheima.domain.User;
import com.itheima.service.UserService;
import org.springframework.cache.annotation.Cacheable;

public class UserServiceImpl implements UserService {
    @Override
    // 开启缓存,将查询到的数据放到id为key的缓存中,value = "cacheSpace"是缓存的位置
    @Cacheable(value = "cacheSpace", key = "#id")
    public User getById(Integer id) {
        return bookDao.selectById(id);
    }
}
java 复制代码
package com.itheima.service.impl;

import com.itheima.domain.User;
import com.itheima.service.UserService;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;

public class UserServiceImpl implements UserService {
    @Override
    // 开启缓存,将查询到的数据放到id为key的缓存中,value = "cacheSpace"是缓存的位置(有读取的功能)
    @Cacheable(value = "cacheSpace", key = "#id")
    @CachePut(value = "cacheSpace", key = "#id")        // 向缓存中放入缓存中
    public User setById(Integer id) {
        return User.selectById(id);
    }

    public User getById(Integer id) {
        return User.selectById(id);
    }

    //查看缓存中是否有id为键的内容,如果有返回查到的内容,没有返回null
    @Cacheable(value = "cacheSpace", key = "#id")
    public String get(Integer id) {
        return null;
    }
}

缓存穿透

缓存穿透是指客户端请求数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库

理解:当一个缓存和数据库中都不存在的数据请求多次被提交,这些请求查缓存失败都会进行查数据库。

解决方法:

  1. 缓存空对象,将不存在的请求缓存成一个null
    优点:实现简单,维护方便
    缺点:
    可能造成短期的不一致
    会产生额外的内存消耗
  2. 布隆过滤

    优点:内存占用较少,没有多余key
    缺点:
    实现复杂
    存在误判的可能

缓存雪崩

缓存雪崩:在同一时段大量的缓存key同时失效,或者redis服务当即,导致大量请求到达数据库带来巨大压力

解决方法:

  1. 给不同的Key的TTL添加随机值
  2. 利用Redis集群提高服务的可用性
  3. 给缓存业务添加降级限流策略
  4. 给业务添加多级缓存

缓存击穿

缓存击穿问题(热点key问题):一个被高并发访问并缓存重建业务交复杂的key突然失效,无数的请求访问会在瞬间给数据库带来巨大的冲击

问题:在缓存失效后,很多用户访问缓存失败,查询数据库,并进行缓存重建(只需一个用户进行缓存重建即可)

解决方案:

  1. 互斥锁
  2. 逻辑过期

互斥锁:

优点:

  1. 没有额外的内存消耗
  2. 保证一致性
  3. 实现简单

缺点:

  1. 线程需要等待,性能受影响
  2. 可能出现死锁

逻辑过期:将线程设置成永远有效,但是设置一个逻辑上的有效期,如果缓存过期了,则先用原来已经过期的缓存用着

优点:线程无需等待,性能较好

缺点:

  1. 不保证一致性
  2. 有额外内存消耗
  3. 实现复杂

互斥锁解决缓存击穿问题

可以通过redis中使用setnx来实现互斥锁

setnx可以对一个变量进行赋值,且只能进行赋值操作和删除操作,上锁就对这个值赋值,释放互斥锁只需要删除这个键值对

setnx进行获取锁

java 复制代码
private boolean tryLock(String key) {
    Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
    return Boolean.TRUE.equals(flag);
}

释放锁

java 复制代码
  private void unlock(String key) {
        stringRedisTemplate.delete(key);
    }

逻辑过期解决缓存击穿问题

缓存更新策略

低一致性需求:使用内存淘汰机制。例如店铺类型的查询缓存

高一致性需求:主动更新,并以超时剔除作为兜底方案。例如店铺详情查询的缓存

好的,图中的内容是关于操作缓存和数据库时需要考虑的三个主要问题:

操作缓存和数据库时有三个问题需要考虑:

  1. 删除缓存还是更新缓存?

    更新缓存: 每次更新数据库都更新缓存,无效写操作较多。

    删除缓存: 更新数据库时让缓存失效,查询时再更新缓存。(更优)

  2. 如何保证缓存与数据库的操作的同时成功或失败?

    单体系统: 将缓存与数据库操作放在一个事务。

    分布式系统: 利用TCC等分布式事务方案。

  3. 先操作缓存还是先操作数据库?

    先删除缓存,再操作数据库。

    先操作数据库,再删除缓存。(更优)

相关推荐
非优秀程序员9 小时前
第04章 邮箱大战,163 与 QQ 的暗战|《互联网产品恩仇录》
java·vue.js·架构
知已难寻10 小时前
Docker 入门:容器技术与 Docker 基础
java·运维·服务器·docker·云原生·容器
文丑颜不良啊12 小时前
LeetCode2481~2500题目解析
java·leetcode
Flynn Liang13 小时前
同一需求三套写法:用 Spring AI、LangChain4j、AgentScope Java 各实现一个带工具调用的 RAG 助手(实战对比)
java·人工智能·大模型·架构设计·spring ai·langchain4j·agentscope
聪明的笨猪猪14 小时前
实际工程问题:AI 生成 Spring Cache(@Cacheable / @CachePut / @CacheEvict)缓存注解踩坑
java·经验分享·面试·ai编程
愿有所息14 小时前
记一次 ARM64 + JDK8 上 fastjson2 的诡异数据错乱
java
秋饼14 小时前
MCP 2026-07-28 无状态协议大改版:Spring AI 2.1 多轮往返请求与网关路由落地
java·ai·技术分享·后端开发
SL_staff15 小时前
JVS-BI 实战:业务人员如何用零代码 ELT + 可视化配置构建实时经营分析闭环
java·spring·全栈
SL_staff16 小时前
企业AI落地难?JVS-AI渐进式实践:安全可控、小切口验证、标准化沉淀与规模化复制
java·开源·aigc
Alluxio17 小时前
Alluxio + OCI:为 AI 工作负载实现亚毫秒访问延迟与可扩展吞吐
运维·数据库·人工智能·分布式·缓存·ai·语言模型