(二)springboot整合redis,基于注解快速实现缓存功能

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 Tair(兼容Redis),内存型 2GB
简介: 缓存主要是将数据存在计算机的内存当中,以便于在使用的时候是可以实现快速读取使用,它的快也是相对于硬盘读取而言。Redis 是一个开源(BSD 许可)的内存中数据结构存储,用作数据库、缓存、消息代理和流引擎。Redis 提供数据结构,例如字符串、散列、列表、集合、带有范围查询的排序集、位图、超日志、地理空间索引和流。redis是一种使用比较广泛、性能强悍的缓存框架,在国内公司的使用量也是很多的。......

前言

对于咱们程序员而言,在考虑使用一个新功能的框架式,我们首先需要弄懂它的定义是什么?为什么要用它?应该怎样用才能更好的实现它的价值?

无论在使用什么框架或者学习新东西的时候都需要遵循这三个问题原则。

1、什么是缓存

缓存主要是将数据存在计算机的内存当中,以便于在使用的时候是可以实现快速读取使用,它的快也是相对于硬盘读取而言。

Redis 是一个开源(BSD 许可)的内存中数据结构存储,用作数据库、缓存、消息代理和流引擎。Redis 提供数据结构,例如字符串、散列、列表、集合、带有范围查询的排序集、位图、超日志、地理空间索引和流。Redis 具有内置复制、 Lua 脚本编写、 LRU 垃圾清理、事务处理和不同级别的磁盘持久性,并通过 Redis Sentinel 提供高可用性和使用 Redis Cluster 自动分区。
为了实现最佳性能,Redis 使用内存中的数据集。根据用例的不同,Redis 可以通过定期将数据集转储到磁盘或将每个命令附加到基于磁盘的日志来持久化数据。如果您只是需要一个功能丰富的、联网的、内存中的缓存,也可以禁用持久性。

redis是一种使用比较广泛、性能强悍的缓存框架,在国内公司的使用量也是很多的。

2、为什么要用缓存

缓存主要是针对读多写少、高并发的应用场景,来实现对数据的快速响应。

使用缓存后的好处如下:

1、提高读取吞吐量

2、提升应用程序性能

3、降低数据库成本

4、减少后端负载

5、消除数据库热点

6、可预测的性能

既然使用缓存的好处那么明显,我们怎么会拒绝这么好的东西呢?

3、怎么使用缓存

整合redis前需要把本地redis或者远程的redis服务先启动起来,可通过点击下载redis到本地运行起来,点击redis-server运行后界面如下图

在这里插入图片描述

我们主要是基于springboot进行使用redis,在使用之前你需要先搭建好springboot的项目,如果不会搭建的朋友可参考这篇博客(一)还不会用springboot写接口?快看这里,手把手操作,一发入魂~

之后咱们所有的功能都是基于该项目进行深层开发,尽可能的让每一为同学都会使用java的框架进行项目开发,入门程序猿领域。

废话不多说,咱们开始干吧。

3.1 引入redis依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

项目完整的pom.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.yinfeng</groupId>
    <artifactId>test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>test</name>
    <description>test</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

3.2 配置redis连接信息并创建redis配置类

在application.yml文件配置redis链接信息,我这里使用的是本地redis服务,所以链接信息是127.0.0.1

spring:
  redis:
    host: 127.0.0.1
    port: 6379

完整的yml文件配置如下

server:
  # 服务端口
  port: 8888
  servlet:
    context-path: /test
spring:
  application:
    name: yinfeng-test
  # 数据库相关配置
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: yinfeng
    driver-class-name: com.mysql.cj.jdbc.Driver
  redis:
    host: 127.0.0.1
    port: 6379

创建redis配置类,一定要注意==打上@EnableCaching注解==,否则spring自带的缓存注解功能将不会自动启用


/**
 * @author yinfeng
 * @description redis配置类
 * @since 2022/5/29 0:03
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {


    /**
     * 自定义配置 RedisTemplate
     * @Primary注解 默认加载此配置 忽略 RedisAutoConfiguration 的 stringRedisTemplate 配置
     * @param factory
     * @return
     */
    @Bean
    @Primary
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        // 我们为了自己开发方便,一般直接使用 <String, Object>
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        // Json序列化配置
        Jackson2JsonRedisSerializer 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);
        // String 的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

    /**
     * 修改 Cacheable 默认序列化方式 使用Redis配置的序列化
     * 解决 @Cacheable 序列化失败 而 RedisUtil可以成功 问题
     * @param redisTemplate RedisTemplate
     * @return RedisCacheManager
     */
    @Bean
    public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory());
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                // 设置默认的超时时间为2小时
                .entryTtl(Duration.ofHours(2))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()))
                // 设置默认的缓存前缀
                .prefixCacheNameWith("CACHE_");
        return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
    }
}

3.3 在controller接口方法上使用注解

使用缓存查询注解。在这里,我们主要是通过获取菜单详情的接口上打了一个缓存查询的注解,然后即可以实现每次查询的时候先判断缓存里面有没有值,有值就直接从缓存里面去,否则再去查数据库
为了方便测试,我把注解直接打在controller层上面。但是大家要注意,在项目中一般要写在==service层(实现类上的)==。

/**
 * @author yinfeng
 * @since 2022年3月12日 下午9:40:48
 * @description 菜单表
 */
@Api(tags = "菜单表")
@RestController
@RequestMapping("/menus")
@Slf4j
public class MenusController{
    @Resource
    private MenusService menusService;
    
        @PostMapping("/getOne")
    @ApiOperation(value = "单个查询", notes = "菜单表")
    @Cacheable(value = "MenusController", key = "'menus-' + #menus.id")
    public Menus getOne(@RequestBody Menus menus) {
        log.info("从数据库读取数据");
        return menusService.getOne(menus);
    }
}

使用缓存更新注解,主要包含新增、更新和删除数据。该缓存更新和删除的原理主要是是在每次接口调用的时候都去更新对应缓存里面key,删除也是通过重新设置一个新的值实现的,等下通过测试,咱们可以很明显的看到结果

/**
 * @author yinfeng
 * @since 2022年3月12日 下午9:40:48
 * @description 菜单表
 */
@Api(tags = "菜单表")
@RestController
@RequestMapping("/menus")
@Slf4j
public class MenusController{
    @Resource
    private MenusService menusService;
    
    @PostMapping("/save")
    @ApiOperation(value = "新增或编辑", notes = "菜单表")
    @CachePut(value = "MenusController", key = "'menus-' + #menus.id")
    public Menus save(@RequestBody Menus menus) {
        return menusService.saveData(menus);
    }

    @PostMapping("/delete")
    @ApiOperation(value = "删除", notes = "菜单表")
    @CacheEvict(value = "MenusController", key = "'menus-' + #menus.id")
    public boolean delete(@RequestBody Menus menus) {
        return menusService.delete(menus);
    }
}

3.4 测试一下

我们通过knife4j接口文档工具调用一下接口进行测试

  1. 查询详情接口测试

我们先通过调用菜单详情的接口,然后可以看到正常返回数据,同时日志里面因为是第一次请求所以直接去查数据库,当第二次请求,打过来的时候,可以看到那个日志已经没了,所以该请求通过缓存取到的数据
在这里插入图片描述
同时,我们通过redis客户端也可以很清晰的看到,已经有一个对应的key和value了,因此可以说明咱们的注解已经生效了
在这里插入图片描述

  1. 更新接口测试

我们首先调用保存的方法去更新咱们这条测试数据,将name改为首页2,通过页面可以看到,咱们已经成功更新了。接下来咱们主要是看缓存里面的数据有没有更新
在这里插入图片描述
当我们再次调用详情接口去查询数据的时候,可以看到数据已经成功更新了,因为咱们的缓存已经有了这个key,而且这个返回的数据是更新后的数据,所以说明咱们缓存里面的数据也已经更新成功了
在这里插入图片描述

  1. 删除接口测试

接下来我们测试一下删除注解,调用接口之后,可以看到放了一个true,说明咱们已经删除数据库的数据成功了。
在这里插入图片描述
再次调用一下详情接口,可以看到该数据已经没有了
在这里插入图片描述
然后再通过redis客户端去查一下该key还在不在,可以很清晰的看到,该key是存在的,但是值已经变了
在这里插入图片描述
上面就是咱们测试的一个全流程,如果有什么疑问和建议欢迎大家留言

4、源码地址

https://gitee.com/yinfeng-code/test.git

5、总结

这篇博客主要是通过springboot简单快捷使用redis,后面咱们可根据该项目逐步开发更多高深的企业级功能,包括starter的封装、数据操作变更日志、响应体包装等,欢迎老铁们追更。

肝文不易,最后希望老铁们给波三连(==点赞、收藏、评论==)加关注,非常感谢大家支持~~

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
目录
相关文章
|
4月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
4月前
|
缓存 NoSQL Java
Redis+Caffeine构建高性能二级缓存
大家好,我是摘星。今天为大家带来的是Redis+Caffeine构建高性能二级缓存,废话不多说直接开始~
637 0
|
1月前
|
存储 NoSQL Redis
采用Redis的Bitmaps实现类似Github连续提交状态的功能。
在现实世界的应用开发中,实现类似于Github提交跟踪系统时,还可能需要考虑用户时区、闰年等日期相关的边界条件,以及辅助数据的存储和查询优化,例如对活跃用户的即时查询和统计等。不过这些都可以在Bitmaps的基础功能之上通过额外的代码逻辑来实现。
62 0
|
4月前
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
168 32
|
4月前
|
存储 监控 NoSQL
使用Redis实现延迟消息发送功能
使用 Redis 的密码认证功能,为实例设置密码以防止未授权访问。为消息提供适当加密,确保消息内容在网络传输过程中不被窃取或篡改。
167 16
|
4月前
|
缓存 NoSQL Java
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
94 5
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
|
6月前
|
缓存 监控 NoSQL
Redis--缓存击穿、缓存穿透、缓存雪崩
缓存击穿、缓存穿透和缓存雪崩是Redis使用过程中可能遇到的常见问题。理解这些问题的成因并采取相应的解决措施,可以有效提升系统的稳定性和性能。在实际应用中,应根据具体场景,选择合适的解决方案,并持续监控和优化缓存策略,以应对不断变化的业务需求。
1050 29
|
6月前
|
缓存 NoSQL Java
Redis应用—8.相关的缓存框架
本文介绍了Ehcache和Guava Cache两个缓存框架及其使用方法,以及如何自定义缓存。主要内容包括:Ehcache缓存框架、Guava Cache缓存框架、自定义缓存。总结:Ehcache适合用作本地缓存或与Redis结合使用,Guava Cache则提供了更灵活的缓存管理和更高的并发性能。自定义缓存可以根据具体需求选择不同的数据结构和引用类型来实现特定的缓存策略。
357 16
Redis应用—8.相关的缓存框架
|
5月前
|
人工智能 缓存 NoSQL
Redis 与 AI:从缓存到智能搜索的融合之路
Redis 已从传统缓存系统发展为强大的 AI 支持平台,其向量数据库功能和 RedisAI 模块为核心,支持高维向量存储、相似性搜索及模型服务。文章探讨了 Redis 在实时数据缓存、语义搜索与会话持久化中的应用场景,并通过代码案例展示了与 Spring Boot 的集成方式。总结来看,Redis 结合 AI 技术,为现代应用提供高效、灵活的解决方案。
|
9月前
|
存储 缓存 NoSQL
解决Redis缓存数据类型丢失问题
解决Redis缓存数据类型丢失问题
357 85