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. 先操作缓存还是先操作数据库?

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

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

相关推荐
陌上丨2 小时前
Redis的Key和Value的设计原则有哪些?
数据库·redis·缓存
曹牧2 小时前
Spring Boot:如何测试Java Controller中的POST请求?
java·开发语言
爬山算法3 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
kfyty7253 小时前
集成 spring-ai 2.x 实践中遇到的一些问题及解决方案
java·人工智能·spring-ai
猫头虎3 小时前
如何排查并解决项目启动时报错Error encountered while processing: java.io.IOException: closed 的问题
java·开发语言·jvm·spring boot·python·开源·maven
李少兄3 小时前
在 IntelliJ IDEA 中修改 Git 远程仓库地址
java·git·intellij-idea
忆~遂愿3 小时前
ops-cv 算子库深度解析:面向视觉任务的硬件优化与数据布局(NCHW/NHWC)策略
java·大数据·linux·人工智能
小韩学长yyds3 小时前
Java序列化避坑指南:明确这4种场景,再也不盲目实现Serializable
java·序列化
仟濹4 小时前
【Java基础】多态 | 打卡day2
java·开发语言
Re.不晚4 小时前
JAVA进阶之路——无奖问答挑战2
java·开发语言