SpringBoot整合Cache缓存技术(二十一)上

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 一. SpringCache一.一 SpringCache 的出现一.二 SpringCache 的简单使用

一. SpringCache

一.一 SpringCache 的出现

在SpringBoot 整合 Redis 时,无论是使用 Lettuce 还是使用 Jedis 连接池, 在查询单个对象,查询全部对象的时候,都是我们自己手动进行判断缓存的信息。

SpringBoot 使用 Lettuce 连接池时:

 @Override
    public User findById(int id) {
        log.info("先从缓存中查询用户编号为{} 是否存在",id);
        User user=redisUtil.get(KEY_PRE+id);
        if(user!=null){
            log.info(">>>>>>>>>>使用的是缓存中的数据");
            return user;
        }
        log.info(">>>>>>>>>>>从数据库中查询,并放置到缓存中");
        user= userMapper.findById(id);
        redisUtil.set(KEY_PRE+id,user);
        return user;
    }
    @Override
    public List<User> findAll() {
        log.info("先从缓存中查询用户列表是否存在");
        List<User> userList= (List<User>) redisUtil.range(KEY_PRE+"ALL");
        if(!CollectionUtils.isEmpty(userList)){
            log.info(">>>>>>>>>>使用的是缓存中的数据");
            return userList;
        }
        log.info(">>>>>>>>>>>从数据库中查询,并放置到缓存中");
        userList= userMapper.findAll();
        redisUtil.leftPushAll(KEY_PRE+"ALL",userList);
        return userList;
    }

SpringBoot 使用 Jedis 连接池时:

 @Override
    public User findById(int id) {
        log.info("先从缓存中查询用户编号为{} 是否存在",id);
        User user=BeanConvertUtil.stringToBean(redisUtil.get(KEY_PRE+id,redisDB),User.class);
        if(user!=null){
            log.info(">>>>>>>>>>使用的是缓存中的数据");
            return user;
        }
        log.info(">>>>>>>>>>>从数据库中查询,并放置到缓存中");
        user= userMapper.findById(id);
        redisUtil.set(KEY_PRE+id,BeanConvertUtil.beanToString(user),redisDB);
        return user;
    }
    @Override
    public List<User> findAll() {
        log.info("先从缓存中查询用户列表是否存在");
        List<String> userStringList= (List<String>) redisUtil.lrange(KEY_PRE+"ALL",0,-1,redisDB);
        List<User> userList=new ArrayList<>();
        if(!CollectionUtils.isEmpty(userStringList)){
            log.info(">>>>>>>>>>使用的是缓存中的数据");
            for(String userString:userStringList){
                userList.add(BeanConvertUtil.stringToBean(userString,User.class));
            }
            return userList;
        }
        log.info(">>>>>>>>>>>从数据库中查询,并放置到缓存中");
        userList= userMapper.findAll();
        for(User user:userList){
            redisUtil.lpush(redisDB,KEY_PRE+"ALL",BeanConvertUtil.beanToString(user));
        }
        return userList;
    }

可以发现, 两个都需要开发者自己手动处理缓存的信息。

并且,如果缓存的工具不同,处理的方式也不同。

实际上,这些与业务是没有太大的联系的。

我们希望能够有一种方式,能够通过简单的配置+注解,达到以前原生的写法就完美了。

    @Override
    public User findById(int id) {
        return userMapper.findById(id);
    }
    @Override
    public List<User> findAll() {
        return userMapper.findAll();
    }

在这两个方法上,添加某个注解, 能够达到 有缓存走缓存,没有缓存走数据库查询,

然后将查询结果放置在缓存中,下一次查询时走缓存的结果,

并且与缓存的实现方式无关 (无论是 Lettuce 还是 Jedis)


有这么一种技术, 叫做SpringCache


一.二 SpringCache 的简单使用

按照 SpringBoot_Redis 项目,创建 SpringBoot_Cache 项目。


Redis服务器打开,使用的仍然是 database 15 数据库。


采用的是 springboot 2.2.13 版本。


目前数据库表 user 里面有三条记录

0.png

一.二.一 pom.xml 添加依赖

        <!--依赖 data-redis的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--不能忘记这个依赖-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <!--添加cache的依赖信息-->

一.二.二 application.yml 进行配置

与redis整合时一样,没有改变。

# 引入 数据库的相关配置
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowMultiQueries=true
    username: root
    password: abc123
  # 配置Redis的使用
  redis:
    database: 15 # 所使用的数据库  默认是0
    host: 127.0.0.1  #所使用的redis的主机地址
    port: 6379  # 端口号  默认是 6379
    password: zk123 # 密码
    timeout: 5000 # 超时时间  5000毫秒
    # 连接池 lettuce 的配置
    lettuce:
      pool:
        max-active: 100
        min-idle: 10
        max-wait: 100000
#整合mybatis时使用的
mybatis:
  #包别名
  type-aliases-package: top.yueshushu.learn.pojo
  #映射文件路径
  mapper-locations: classpath:mybatis/mapper/**/*.xml
  configuration:
    #日志信息
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

一.二.三 启动类上 添加 @EnableCaching 注解

需要在启动类上 添加 @EnableCaching 注解, 开启缓存。

@MapperScan("top.yueshushu.learn.mapper")
@SpringBootApplication
//开启缓存
@EnableCaching 
public class RedisApplication {
    public static void main(String[] args) {
        SpringApplication.run(RedisApplication.class,args);
        System.out.println("运行 Redis Cache缓存");
    }
}

一.二.四 不使用缓存时处理

一.二.四.一 查询 findById 实现

    @Override
    public User findById(int id) {
        return userMapper.findById(id);
    }

一.二.四.二 查询测试

  @Test
    public void findByIdTest(){
        User user=userService.findById(40); //id随时更换
        log.info(user);
    }

运行测试方法 findByIdTest()

第一次查询

1.png

发现查询了数据库

第二次查询

2.png

依然走的是数据库查询.

这是以前的常规的写法。

一.二.五 使用SpringCache 缓存时处理

一.二.五.一 查询 findById 实现

    @Override
    // 指定了参数为 id:  变成了:  value::id 的key值
    @Cacheable(value=KEY_PRE,key = "#id")
    public User findById(int id) {
        return userMapper.findById(id);
    }

在方法上 添加了一个注解 @Cacheable ,补充属性信息

value 表示使用的缓存组, key 表示缓存的值。

一.二.五.二 查询测试

  @Test
    public void findByIdTest(){
        User user=userService.findById(40); //id随时更换
        log.info(user);
    }

运行测试方法 findByIdTest()

第一次查询

3.png

发现查询了数据库

第二次查询

4.png

发现,并没有查询数据库,走的是缓存里面的数据。

查看 Redis客户端

5.png

发现存储的数据乱码了.

一.二.六 处理存储信息乱码问题

除了 RedisConfig.java 配置之外 ,再添加一个 CacheConfig.java 的配置信息

package top.yueshushu.learn.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.log4j.Log4j2;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import javax.annotation.Resource;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
/**
 * @author :zk_yjl
 * @description:Cache的缓存配置信息,可以解决乱码问题
 * @date :2021/09/23 17:09
 */
@Log4j2
@Configuration
public class CacheConfig extends CachingConfigurerSupport {
    @Resource
    private RedisConnectionFactory factory;
    /**
     * 自定义生成redis-key
     *
     * @return
     */
    @Override
    @Bean
    public KeyGenerator keyGenerator() {
        return (o, method, objects) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(o.getClass().getName()).append(".");
            sb.append(method.getName()).append(".");
            for (Object obj : objects) {
                sb.append(obj.toString());
            }
            log.info("keyGenerator=" + sb.toString());
            return sb.toString();
        };
    }
    @Bean
    @Override
    public CacheResolver cacheResolver() {
        return new SimpleCacheResolver(cacheManager());
    }
    @Bean
    @Override
    public CacheErrorHandler errorHandler() {
        // 用于捕获从Cache中进行CRUD时的异常的回调处理器。
        return new SimpleCacheErrorHandler();
    }
    @Bean
    @Override
    public CacheManager cacheManager() {
        return new RedisCacheManager(
                RedisCacheWriter.nonLockingRedisCacheWriter(factory),
                this.getRedisCacheConfigurationWithTtl(30*60), // 默认策略,未配置的 key 会使用这个
                this.getRedisCacheConfigurationMap() // 指定 key 策略
        );
    }
    private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
        //DayCache和SecondsCache进行过期时间配置translates缓存丢弃改为了redis
        redisCacheConfigurationMap.put("translates", this.getRedisCacheConfigurationWithTtl(12*60*60));
        redisCacheConfigurationMap.put("strategies", this.getRedisCacheConfigurationWithTtl(60));
        return redisCacheConfigurationMap;
    }
    private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
        redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
                RedisSerializationContext
                        .SerializationPair
                        .fromSerializer(jackson2JsonRedisSerializer)
        ).entryTtl(Duration.ofSeconds(seconds));
        return redisCacheConfiguration;
    }
}

重新运行测试 (此时缓存信息并没有清空)

6.png

出现了异常.

将缓存信息 key 清空后再执行, 运行是成功的,

从数据库里面查询, 将查询结果放置到Redis缓存里面,并且缓存信息正常展示。

7.png

相关文章
|
8月前
|
机器学习/深度学习 存储 缓存
加速LLM大模型推理,KV缓存技术详解与PyTorch实现
大型语言模型(LLM)的推理效率是AI领域的重要挑战。本文聚焦KV缓存技术,通过存储复用注意力机制中的Key和Value张量,减少冗余计算,显著提升推理效率。文章从理论到实践,详细解析KV缓存原理、实现与性能优势,并提供PyTorch代码示例。实验表明,该技术在长序列生成中可将推理时间降低近60%,为大模型优化提供了有效方案。
1508 15
加速LLM大模型推理,KV缓存技术详解与PyTorch实现
|
4月前
|
缓存 监控 Linux
Linux系统清理缓存(buff/cache)的有效方法。
总结而言,在大多数情形下你不必担心Linux中buffer与cache占用过多内存在影响到其他程序运行;因为当程序请求更多内存在没有足够可用资源时,Linux会自行调整其占有量。只有当你明确知道当前环境与需求并希望立即回收这部分资源给即将运行重负载任务之前才考虑上述方法去主动干预。
1611 10
|
5月前
|
Java 数据库连接 API
Java 8 + 特性及 Spring Boot 与 Hibernate 等最新技术的实操内容详解
本内容涵盖Java 8+核心语法、Spring Boot与Hibernate实操,按考试考点分类整理,含技术详解与代码示例,助力掌握最新Java技术与应用。
162 2
|
8月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
517 70
|
6月前
|
缓存 负载均衡 网络协议
电商API接口性能优化技术揭秘:缓存策略与负载均衡详解
电商API接口性能优化是提升系统稳定性和用户体验的关键。本文聚焦缓存策略与负载均衡两大核心,详解其在电商业务中的实践。缓存策略涵盖本地、分布式及CDN缓存,通过全量或部分缓存设计和一致性维护,减少后端压力;负载均衡则利用反向代理、DNS轮询等技术,结合动态调整与冗余部署,提高吞吐量与可用性。文中引用大型及跨境电商平台案例,展示优化效果,强调持续监控与迭代的重要性,为电商企业提供了切实可行的性能优化路径。
|
5月前
|
存储 缓存 NoSQL
Spring Cache缓存框架
Spring Cache是Spring体系下的标准化缓存框架,支持多种缓存(如Redis、EhCache、Caffeine),可独立或组合使用。其优势包括平滑迁移、注解与编程两种使用方式,以及高度解耦和灵活管理。通过动态代理实现缓存操作,适用于不同业务场景。
457 0
|
7月前
|
缓存 Java 数据库
SpringBoot集成Ehcache缓存使用指南
以上是SpringBoot集成Ehcache缓存的基本操作指南,帮助你在实际项目中轻松实现缓存功能。当然,Ehcache还有诸多高级特性,通过学习和实践,你可以更好地发挥它的威力。
708 20
|
缓存 NoSQL Java
什么是缓存?如何在 Spring Boot 中使用缓存框架
什么是缓存?如何在 Spring Boot 中使用缓存框架
800 0
|
11月前
|
缓存 NoSQL Java
springboot怎么使用rides缓存方法的返回值 完整例子
通过上述步骤,我们成功地在 Spring Boot 项目中集成了 Redis 缓存,并通过注解的方式实现了方法返回值的缓存。这种方式不仅提高了系统的性能,还简化了缓存管理的复杂度。使用 Spring Boot 的缓存注解和 Redis,可以轻松地实现高效、可靠的缓存机制。
253 23
|
12月前
|
数据采集 存储 缓存
如何使用缓存技术提升Python爬虫效率
如何使用缓存技术提升Python爬虫效率