Redis之与SSM集成Spring注解式缓存

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis之与SSM集成Spring注解式缓存

🎉🎉欢迎来到我的CSDN主页!🎉🎉

🏅我是君易--鑨,一个在CSDN分享笔记的博主。📚📚

🌟推荐给大家我的博客专栏《Redis实战开发》。🎯🎯

🎁如果感觉还不错的话请给我关注加三连吧!🎁🎁

💖期待你的加入,一起学习,一起进步!💖💖



前言

        在Redis系列的前面两期博客中带大家了解了有关Redis在Linux及Windows两个系统的安装及使用,然后有分享了java中如何去使用Redis操作Redis数据库。这期博客带大家一起了解SSM中集成Redis及其中的用法。

一、Redis集成到SSM中

1. 导入pom.xml依赖配置

       在项目的pom.xml中导入Redis的相关依赖。

        首先在properties标签中添加以下代码

<redis.version>2.9.0</redis.version>
    <redis.spring.version>1.7.1.RELEASE</redis.spring.version>

       然后在 dependencies的标签中添加以下依赖

<dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>${redis.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-redis</artifactId>
      <version>${redis.spring.version}</version>
    </dependency>

        到此我们的pom.xml文件的配置就告一段落了

2. 注册一个redis.properties

       这个redis.properties文件是用于配置连接Redis客户端的文件,类似于我们之前配置MySQL数据库连接的jdbc.properties,用于编写连接的端口号、端口、账号、密码、最好连接数等等之类的,该文件也是用于后面有关redis.xml文件的配置。

redis.hostName=127.0.0.1--->(数据库名)
redis.port=6379    --->(端口号)
redis.password=123456  ----->(密码)
redis.timeout=10000
redis.maxIdle=300
redis.maxTotal=1000
redis.maxWaitMillis=1000
redis.minEvictableIdleTimeMillis=300000
redis.numTestsPerEvictionRun=1024
redis.timeBetweenEvictionRunsMillis=30000
redis.testOnBorrow=true
redis.testWhileIdle=true
redis.expiration=3600

3. 新建spring-redis.xml文件

       这是文件的作用是将Redis配置到我们的项目中去,以及读取redis.properties文件的信息。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/cache
       http://www.springframework.org/schema/cache/spring-cache.xsd">
    <!-- 1. 引入properties配置文件 -->
    <!--<context:property-placeholder location="classpath:redis.properties" />-->
    <!-- 2. redis连接池配置-->
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!--最大空闲数-->
        <property name="maxIdle" value="${redis.maxIdle}"/>
        <!--连接池的最大数据库连接数  -->
        <property name="maxTotal" value="${redis.maxTotal}"/>
        <!--最大建立连接等待时间-->
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}"/>
        <!--逐出连接的最小空闲时间 默认1800000毫秒(30分钟)-->
        <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}"/>
        <!--每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3-->
        <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}"/>
        <!--逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1-->
        <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}"/>
        <!--是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个-->
        <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
        <!--在空闲时检查有效性, 默认false  -->
        <property name="testWhileIdle" value="${redis.testWhileIdle}"/>
    </bean>
    <!-- 3. redis连接工厂 -->
    <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
          destroy-method="destroy">
        <property name="poolConfig" ref="poolConfig"/>
        <!--IP地址 -->
        <property name="hostName" value="${redis.hostName}"/>
        <!--端口号  -->
        <property name="port" value="${redis.port}"/>
        <!--如果Redis设置有密码  -->
        <property name="password" value="${redis.password}"/>
        <!--客户端超时时间单位是毫秒  -->
        <property name="timeout" value="${redis.timeout}"/>
    </bean>
    <!-- 4. redis操作模板,使用该对象可以操作redis
        hibernate课程中hibernatetemplete,相当于session,专门操作数据库。
    -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="connectionFactory"/>
        <!--如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String!!  -->
        <property name="keySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        </property>
        <property name="valueSerializer">
            <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
        </property>
        <property name="hashKeySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        </property>
        <property name="hashValueSerializer">
            <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
        </property>
        <!--开启事务  -->
        <property name="enableTransactionSupport" value="true"/>
    </bean>
    <!--  5.配置缓存管理器  -->
    <bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
        <constructor-arg name="redisOperations" ref="redisTemplate"/>
        <!--redis缓存数据过期时间单位秒-->
        <property name="defaultExpiration" value="${redis.expiration}"/>
        <!--是否使用缓存前缀,与cachePrefix相关-->
        <property name="usePrefix" value="true"/>
        <!--配置缓存前缀名称-->
        <property name="cachePrefix">
            <bean class="org.springframework.data.redis.cache.DefaultRedisCachePrefix">
                <constructor-arg index="0" value="-cache-"/>
            </bean>
        </property>
    </bean>
    <!--6.配置缓存生成键名的生成规则-->
    <bean id="cacheKeyGenerator" class="com.zking.ssm.redis.CacheKeyGenerator"></bean>
    <!--7.启用缓存注解功能-->
    <cache:annotation-driven cache-manager="redisCacheManager" key-generator="cacheKeyGenerator"/>
</beans>

注意事项:

1.配置.properties文件注册事项

       当spring-context.xml文件中需要注册两个及两个以上的.peoperties文件时,不能在spring-*.xml中注册,因此只能在spring-context.xml文件中配置。因为一但分开单独注册则又一个文件的信息读取不到。

       因此我们会将spring-*.xml文件的注册代码注释掉(以spring-redis.xml为例)

       然后我们会在 spring-context.xml文件中进行总的设置配置

2. pom.xm配置注意事项

       注意pom.xml文件中的resource标签的配置信息,resources的配置必须涵盖读取.properties结尾的文件。

3.  redisTemplate的使用

redisTemplate的使用可以参照jdbcTemplate/amqpTemplate...的使用,也就是序列化的配置

配置数据源

配置连接工厂

配置序列化器

配置缓存管理器

       redis中有很多数据,例如:用户数据、数据字典数据、系统设置数据等,不同的数据有着不同的有效时间,用于设置时长当然这只是其中之一;也可以用于设置缓存的前缀

配置缓存生成键名的生成规则

CacheKeyGenerator.java
package com.zking.ssm.redis;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.util.ClassUtils;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
@Slf4j
public class CacheKeyGenerator implements KeyGenerator {
    // custom cache key
    public static final int NO_PARAM_KEY = 0;
    public static final int NULL_PARAM_KEY = 53;
    @Override
    public Object generate(Object target, Method method, Object... params) {
        StringBuilder key = new StringBuilder();
        key.append(target.getClass().getSimpleName()).append(".").append(method.getName()).append(":");
        if (params.length == 0) {
            key.append(NO_PARAM_KEY);
        } else {
            int count = 0;
            for (Object param : params) {
                if (0 != count) {//参数之间用,进行分隔
                    key.append(',');
                }
                if (param == null) {
                    key.append(NULL_PARAM_KEY);
                } else if (ClassUtils.isPrimitiveArray(param.getClass())) {
                    int length = Array.getLength(param);
                    for (int i = 0; i < length; i++) {
                        key.append(Array.get(param, i));
                        key.append(',');
                    }
                } else if (ClassUtils.isPrimitiveOrWrapper(param.getClass()) || param instanceof String) {
                    key.append(param);
                } else {//Java一定要重写hashCode和eqauls
                    key.append(param.hashCode());
                }
                count++;
            }
        }
        String finalKey = key.toString();
//        IEDA要安装lombok插件
        log.debug("using cache key={}", finalKey);
        return finalKey;
    }
}

4. spring-context.xml中配置

二、Redis注解开发及应用场景

1. 未使用注解开发

       我们编写一个测试类用于测试模拟从数据库中获取数据,以此来实现模拟数据库操作。

ClazzBizTest.java

package com.zking.shiro;
import com.zking.ssm.biz.ClazzBiz;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
 * @author小李飞刀
 * @site www.javaxl.com
 * @company xxx公司
 * @create  2022-10-26 15:29
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:applicationContext.xml"})
public class ClazzBizTest {
    @Autowired
    private ClazzBiz clazzBiz;
    @Test
    public void test1(){
        System.out.println(clazzBiz.selectByPrimaryKey(10));
        System.out.println(clazzBiz.selectByPrimaryKey(10));
    }
    @Test
    public void test2(){
        clazzBiz.deleteByPrimaryKey(10);
    }
}

       当我们执行测试test1()方法时我们可以通过查看控制台输出的结果得知当我们调用两次查询方法时执行了两次数据库操作。(控制台输出如下图)

       接下来我们一起来看看使用Redis注解运行后的方法及应用场景

2. @Cacheable

2.1 概述及作用

       @Cacheable用于具有返回值的方法,可以保存缓存键值对。一般用于查询操作。一般配置在方法或类上。简单的说就是即写入也读取。

      作用:本方法执行后,先去缓存看有没有数据,如果没有,从数据库中查找出来,给缓存中存一份,返回结果, 下次本方法执行,在缓存未过期情况下,先在缓存中查找,有的话直接返回,没有的话从数据库查找。

2.2 注解参数说明

value:缓存位置的一段名称,不能为空

key:缓存的key,默认为空,表示使用方法的参数类型及参数值作为key,支持SpEL

condition:触发条件,满足条件就加入缓存,默认为空,表示全部都加入缓存,支持SpEL

2.3 案例演示

携带value参数的使用

       然后我们再在测试类去测试该方法,查看控制台的输出内容,看看是否与未使用注解式标签是一样。

        由此看到第二次时我们访问的是缓存,如果我们再执行一次则一次都不会访问操作数据库,而是访问我们的缓存。

我们可以在Redis客户端上查看我们的缓存数据

携带包含key参数的使用

       使用了key参数是表自定义生成key

       当我们再次去执行被改标签使用了的方法时,会在Redis缓存库中重新生产一个根据自己定义的key。

携带 condition参数的使用

       condition代表的是触发执行的条件,满足条件就执行方法加入缓存

       当我们分别执行一个符合条件一个不符合条件时,到redis缓存库中查看

情况一:符合条件

       查看redis的缓存库

情况二:不符合触发条件

       由图可知,当我们未达到触发条件执行该方法则不会生成数据缓存在我们的redis库中生成数据缓存。

3.  @CachePut

3.1 概述及作用

       与@Cacheable类似,也可以保存缓存键值对。一般用于插入和修改操作。简单的说只写入不读取。

       作用:适用于写入或更新操作,需要将结果放入缓存并确保缓存的数据是最新的。每次调用被@CachePut注解的方法时,都会执行方法体内的逻辑,并将结果存入缓存中。

3.2 参数说明

value :  缓存的名称,在 spring 配置文件中定义,必须指定至少一个

key :  缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合

condition  :缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存

3.3 案例演示

       参数和Cacheable的用法一致,所以我来演示一下CachePut与Cacheable的区别

4. @CacheEvict

4.1 概述及作用

       用于void类型的方法,不需要保存任何值,一般用于删除缓存key的操作。

4.2 参数说明

value:缓存位置的一段名称,不能为空

key:缓存的key,默认为空,表示使用方法的参数类型及参数值作为key,支持SpEL

condition:触发条件,满足条件就加入缓存,默认为空,表示全部都加入缓存,支持SpEL

allEntries:true表示清除value中的全部缓存,默认为false

4.3 案例演示

       首先我们添加几条数据到我们的redis缓存数据库中,方便我们测试使用

       运行该方法在去查看redis缓存数据库

       我们在redis客户端刷新数据库查看一下就会发现我们之前存储的数据被清理掉了

三、Redis的三种极端现象

描述图

1. 击穿

概述

       在Redis中,击穿是指一个热点key,在高并发的请求下,由于这个key失效的瞬间,持续的高并发直接请求数据库。具体来说,当某个热点key失效或者被删除,而此时恰好有大量的请求正在访问这个key,这些请求就会直接穿透到数据库中,导致数据库压力骤增,甚至可能引发数据库崩溃等问题。

解决方案:

  • 限流:请求redis之前做流量削峰
  • 设置热点key永不过期
  • 设置锁
  1. 1.获取 Redis 锁,如果没有获取到,则回到任务队列继续排队
  2. 2.获取到锁,从数据库拉取数据并放入缓存中
  3. 3.释放锁,其他请求从缓存中拿到数据

2. 穿透

概述

       在Redis中,缓存穿透是指查询一个根本不存在的数据,缓存层没有命中,然后去查数据库(持久层),数据库(持久层)也没有命中。 通常如果从存储层查不到数据则不写入缓存层。 比如:用户不断发起请求,通过文章的id来获取文章,如果这个id没有对应的数据,则每次都会请求到数据库。 如果这个用户是攻击者,在请求过多时会导致数据库压力过大,严重会击垮数据库。        

解决方案

1.规则排除

       可以增加一些参数检验。例如数据库数据 id 一般都是递增的,如果请求 id = -10 这种参数,势必绕过Redis。避免这种情况,可以对用户真实性检验等操作。

2.null值填充

       当缓存穿透时,redis存入一个类似null的值,下次访问则直接缓存返回空,当数据库中存在该数据的值则需要把redis存在的null值清除并载入新值,此方案不能解决频繁随机不规则的key请求。

3.双重检测加锁

       Redis在并发发情况下,发生多次访问数据库,可以使用双重检测加锁机制防止缓存击穿。

4.使用布隆过滤器

       布隆过滤器是一种数据结构,可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。如果想要判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定。同时检索速度也越来越慢。使用布隆过滤器可以避免将所有可能缓存的数据都保存下来,只需要将可能的数据保存即可,从而节省空间。

3. 雪崩

概述

       在Redis中,雪崩是指缓存层发生故障或失效时,所有请求都会直接到达数据库层,导致数据库压力剧增,甚至崩溃的情况。缓存雪崩通常是由于缓存层承载了大量的请求,有效的保护了存储层,但如果缓存由于某些原因整体不能够提供服务,所有的请求就会到达存储层,存储层的调用量就会暴增,从而引发雪崩现象。缓存雪崩可能会发生在缓存服务器重启或者大量缓存集中在某一个时间段失效的情况下,此时大量数据会去直接访问数据库,给数据库带来很大的压力。

解决方案

  • 给不同的热点key设置不同的缓存策略
  • 使用分布式缓存
  • 使用锁来更新key

本期的Redis系列博客分享到此结束啦

希望老铁关注加三连

这是对博客最大的支持鼓励

相关实践学习
基于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
目录
相关文章
|
28天前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
|
9天前
|
存储 NoSQL Java
使用lock4j-redis-template-spring-boot-starter实现redis分布式锁
通过使用 `lock4j-redis-template-spring-boot-starter`,我们可以轻松实现 Redis 分布式锁,从而解决分布式系统中多个实例并发访问共享资源的问题。合理配置和使用分布式锁,可以有效提高系统的稳定性和数据的一致性。希望本文对你在实际项目中使用 Redis 分布式锁有所帮助。
28 5
|
29天前
|
存储 缓存 NoSQL
【赵渝强老师】基于Redis的旁路缓存架构
本文介绍了引入缓存后的系统架构,通过缓存可以提升访问性能、降低网络拥堵、减轻服务负载和增强可扩展性。文中提供了相关图片和视频讲解,并讨论了数据库读写分离、分库分表等方法来减轻数据库压力。同时,文章也指出了缓存可能带来的复杂度增加、成本提高和数据一致性问题。
【赵渝强老师】基于Redis的旁路缓存架构
|
1月前
|
缓存 NoSQL Redis
Redis 缓存使用的实践
《Redis缓存最佳实践指南》涵盖缓存更新策略、缓存击穿防护、大key处理和性能优化。包括Cache Aside Pattern、Write Through、分布式锁、大key拆分和批量操作等技术,帮助你在项目中高效使用Redis缓存。
201 22
|
22天前
|
缓存 NoSQL PHP
Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出
本文深入探讨了Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出。文章还介绍了Redis在页面缓存、数据缓存和会话缓存等应用场景中的使用,并强调了缓存数据一致性、过期时间设置、容量控制和安全问题的重要性。
36 5
|
1月前
|
消息中间件 NoSQL Java
Spring Boot整合Redis
通过Spring Boot整合Redis,可以显著提升应用的性能和响应速度。在本文中,我们详细介绍了如何配置和使用Redis,包括基本的CRUD操作和具有过期时间的值设置方法。希望本文能帮助你在实际项目中高效地整合和使用Redis。
45 2
|
1月前
|
缓存 NoSQL 中间件
redis高并发缓存中间件总结!
本文档详细介绍了高并发缓存中间件Redis的原理、高级操作及其在电商架构中的应用。通过阿里云的角度,分析了Redis与架构的关系,并展示了无Redis和使用Redis缓存的架构图。文档还涵盖了Redis的基本特性、应用场景、安装部署步骤、配置文件详解、启动和关闭方法、systemctl管理脚本的生成以及日志警告处理等内容。适合初学者和有一定经验的技术人员参考学习。
168 7
|
1月前
|
存储 缓存 监控
利用 Redis 缓存特性避免缓存穿透的策略与方法
【10月更文挑战第23天】通过以上对利用 Redis 缓存特性避免缓存穿透的详细阐述,我们对这一策略有了更深入的理解。在实际应用中,我们需要根据具体情况灵活运用这些方法,并结合其他技术手段,共同保障系统的稳定和高效运行。同时,要不断关注 Redis 缓存特性的发展和变化,及时调整策略,以应对不断出现的新挑战。
64 10
|
1月前
|
JavaScript NoSQL Java
CC-ADMIN后台简介一个基于 Spring Boot 2.1.3 、SpringBootMybatis plus、JWT、Shiro、Redis、Vue quasar 的前后端分离的后台管理系统
CC-ADMIN后台简介一个基于 Spring Boot 2.1.3 、SpringBootMybatis plus、JWT、Shiro、Redis、Vue quasar 的前后端分离的后台管理系统
38 0
|
2月前
|
Java Maven Docker
gitlab-ci 集成 k3s 部署spring boot 应用
gitlab-ci 集成 k3s 部署spring boot 应用