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

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云数据库 RDS MySQL Serverless,价值2615元额度,1个月
简介: 缓存主要是将数据存在计算机的内存当中,以便于在使用的时候是可以实现快速读取使用,它的快也是相对于硬盘读取而言。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数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
5天前
|
存储 消息中间件 缓存
Redis缓存技术详解
【5月更文挑战第6天】Redis是一款高性能内存数据结构存储系统,常用于缓存、消息队列、分布式锁等场景。其特点包括速度快(全内存存储)、丰富数据类型、持久化、发布/订阅、主从复制和分布式锁。优化策略包括选择合适数据类型、设置过期时间、使用Pipeline、开启持久化、监控调优及使用集群。通过这些手段,Redis能为系统提供高效稳定的服务。
|
1天前
|
缓存 NoSQL 应用服务中间件
Redis多级缓存
Redis多级缓存
7 0
|
1天前
|
缓存 NoSQL 关系型数据库
Redis 缓存 一致性
Redis 缓存 一致性
5 0
|
1天前
|
缓存 监控 NoSQL
Redis经典问题:缓存穿透
本文介绍了缓存穿透问题在分布式系统和缓存应用中的严重性,当请求的数据在缓存和数据库都不存在时,可能导致数据库崩溃。为解决此问题,提出了五种策略:接口层增加校验、缓存空值、使用布隆过滤器、数据库查询优化和加强监控报警机制。通过这些方法,可以有效缓解缓存穿透对系统稳定性的影响。
47 3
|
2天前
|
JSON NoSQL Java
深入浅出Redis(十三):SpringBoot整合Redis客户端
深入浅出Redis(十三):SpringBoot整合Redis客户端
|
2天前
|
缓存 NoSQL 搜索推荐
Redis缓存雪崩穿透等解决方案
本文讨论了缓存使用中可能出现的问题及其解决方案。首先,缓存穿透是指查询数据库中不存在的数据,导致请求频繁到达数据库。解决方法包括数据校验、缓存空值和使用BloomFilter。其次,缓存击穿是大量请求同一失效缓存项,可采取监控、限流或加锁策略。再者,缓存雪崩是大量缓存同时失效,引发数据库压力。应对措施是避免同一失效时间,分散缓存过期。接着,文章介绍了Spring Boot中Redis缓存的配置,包括缓存null值以防止穿透,并展示了自定义缓存过期时间的实现,以避免雪崩效应。最后,提供了在`application.yml`中配置不同缓存项的个性化过期时间的方法。
|
2天前
|
消息中间件 缓存 NoSQL
Redis经典问题:缓存雪崩
本文介绍了Redis缓存雪崩问题及其解决方案。缓存雪崩是指大量缓存同一时间失效,导致请求涌入数据库,可能造成系统崩溃。解决方法包括:1) 使用Redis主从复制和哨兵机制提高高可用性;2) 结合本地ehcache缓存和Hystrix限流降级策略;3) 设置随机过期时间避免同一时刻大量缓存失效;4) 使用缓存标记策略,在标记失效时更新数据缓存;5) 实施多级缓存策略,如一级缓存失效时由二级缓存更新;6) 通过第三方插件如RocketMQ自动更新缓存。这些策略有助于保障系统的稳定运行。
96 1
|
11天前
|
存储 缓存 NoSQL
【Go语言专栏】Go语言中的Redis操作与缓存应用
【4月更文挑战第30天】本文探讨了在Go语言中使用Redis进行操作和缓存应用的方法。文章介绍了Redis作为高性能键值存储系统,用于提升应用性能。推荐使用`go-redis/redis`库,示例代码展示了连接、设置、获取和删除键值对的基本操作。文章还详细阐述了缓存应用的步骤及常见缓存策略,包括缓存穿透、缓存击穿和缓存雪崩的解决方案。利用Redis和合适策略可有效优化应用性能。
|
14天前
|
存储 缓存 NoSQL
Redis多级缓存指南:从前端到后端全方位优化!
本文探讨了现代互联网应用中,多级缓存的重要性,特别是Redis在缓存中间件的角色。多级缓存能提升数据访问速度、系统稳定性和可扩展性,减少数据库压力,并允许灵活的缓存策略。浏览器本地内存缓存和磁盘缓存分别优化了短期数据和静态资源的存储,而服务端本地内存缓存和网络内存缓存(如Redis)则提供了高速访问和分布式系统的解决方案。服务器本地磁盘缓存因I/O性能瓶颈和复杂管理而不推荐用于缓存,强调了内存和网络缓存的优越性。
40 1
|
6天前
|
缓存 监控 NoSQL
Redis缓存雪崩及应对策略
缓存雪崩是分布式系统中一个常见但危险的问题,可以通过合理的缓存策略和系统设计来降低发生的概率。采用多层次的缓存架构、缓存预热、合理的缓存失效时间等措施,都可以有效应对缓存雪崩,提高系统的稳定性和性能。在实际应用中,及时发现并解决潜在的缓存雪崩问题,是保障系统可用性的关键一环。
37 14