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

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

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

相关推荐
hygge9991 小时前
synchronized vs CopyOnWrite 系列
java·开发语言·经验分享·面试
用户0304805912631 小时前
前后端数据传输: 利用 Jackson 注解实现 Enum 与 int 的双向映射
java·后端
Rhys..1 小时前
Jenkinsfile保存在项目根目录下的好处
java·开发语言
讨厌下雨的天空2 小时前
线程同步与互斥
java·开发语言
IUGEI2 小时前
【计算机网络】HTTP/3如何实现可靠传输?
java·网络·后端·网络协议·tcp/ip·计算机网络·http
工具人55553 小时前
下载文件wget
数据库·redis·缓存
u***u6853 小时前
JavaGraphQL案例
java·spring boot·后端
2501_941879813 小时前
Python在微服务高并发异步流量控制与动态限流熔断架构中的实践
java·开发语言
REDcker4 小时前
C++ std::shared_ptr 线程安全性和最佳实践详解
java·jvm·c++