SSM之spring注解式缓存redis

SSM(Spring + SpringMVC + MyBatis)是一种常用的 Java Web 开发框架,而 Redis 是一种常用的 NoSQL 数据库。在 SSM 框架中,可以通过整合 Redis 来实现数据缓存、分布式锁等功能,提高系统的性能和可靠性。

SSM整合redis

Redis 和 MySQL 是两种不同类型的数据库,下面是它们之间的主要区别:

  1. 数据存储方式:Redis 是一种基于键值对存储的内存数据库,而 MySQL 是一种基于表的关系型数据库。

  2. 数据查询语言:Redis 是一种 NoSQL 数据库,不支持 SQL 语言,没有像 SQL 那样的复杂查询语句,而 MySQL 支持 SQL 语言,可以进行复杂的查询操作。

  3. 数据存储方式:Redis 数据以键值对的方式进行存储,可以存储多种数据类型,如字符串、哈希、列表、集合、有序集合等。MySQL 则基于表的数据模型进行存储,支持多种数据类型,如整型、字符型、日期型等。

  4. 数据库容量:Redis 基于内存存储,数据容量有限,适用于存储较小量的数据。MySQL 支持大容量的存储,适合存储大量数据。

  5. 数据库性能:Redis 基于内存存储,读写速度极快,通常可以支持高并发访问,适合实时数据读写操作。MySQL 则需要通过硬盘进行存储和读写操作,速度相对较慢,但对于复杂的数据处理,MySQL 的性能通常比 Redis 更优。

综上所述,Redis 和 MySQL 分别适用于不同的应用场景,需要结合具体业务需求进行选择。

可以去参考整合mysql,即整合mybatis

1.pom配置

2.spring-mydatis.xml -->mybatis.cfg.xml

1.包扫描

2.注册一个jdbc.properties

3.配置数据源(数据连接池)

4.配置sqlsession,配置会话

5.配置事务。。。

3.springContext.xml中添加spring-mybatis.xml

复制代码
<?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="300"/>
        <!--连接池的最大数据库连接数  -->
        <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>

整合redis

  1. pom配置

2.spring-redis.xml

1.注册了一个redis.properties

2.配置数据源(数据库连接池)

3.连接工厂

4.配置序列化器

5.配置redis的key生成策略

3.springContext.xml中添加spring-redis.xml

导入

导入我们的SSM项目,在我们的导入项目的时候开启服务

查看我们的配置

注意1:当spring-comtext.xml中需要注册多个.properties结尾的配置文件,那么不能在spring-*.xml添加注册

注意2:resources的配置必须要涵盖读取.properties结尾的文件

注意3:redisTemplate的使用,可以参照jdbcTemplate,new Jedi(ip,port).auth().get

pom
复制代码
  <properties>    
    <redis.version>2.9.0</redis.version>
    <redis.spring.version>1.7.1.RELEASE</redis.spring.version>
  </properties>
 
  <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>
  </dependencies>
redis.properties配置
复制代码
redis.hostName=47.100.191.44
redis.port=6379
redis.password=xiaoli_redis
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

applicationContext.xml

复制代码
<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--1. 引入外部多文件方式 -->
    <bean id="propertyConfigurer"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
        <property name="ignoreResourceNotFound" value="true" />
        <property name="locations">
            <list>
                <value>classpath:jdbc.properties</value>
                <value>classpath:redis.properties</value>
            </list>
        </property>
    </bean>
 
<!--  随着后续学习,框架会越学越多,不能将所有的框架配置,放到同一个配制间,否者不便于管理  -->
    <import resource="applicationContext-mybatis.xml"></import>
    <import resource="spring-redis.xml"></import>
    <import resource="applicationContext-shiro.xml"></import>
</beans>

注解式开发及应用场景

注解式开发是一种基于注解的编程模型,它允许在代码中声明注解,以此来描述应用程序的元素和行为。注解式开发可以提高代码的可读性、可维护性和可扩展性,常用于各种框架和库中,如Spring、Hibernate、JUnit等。

以下是注解式开发应用的一些场景:

  1. 配置和管理对象声明:通过注解来描述对象的属性和行为,可以减少配置文件的数量和复杂度,同时也提高了代码的可读性和可维护性。

  2. 实现声明式事务处理:在方法上使用注解来声明事务的属性和行为,可以使事务处理更加简单和可控。

  3. 实现依赖注入:注解可以用于标记需要注入的对象或属性,从而使依赖注入更加简单和灵活。

  4. 执行AOP切面:通过注解来声明切面和切点,可以在运行时动态地织入切面。

  5. 描述测试用例:通过注解来描述测试用例的属性和行为,可以使测试代码更加简洁和可读。

总之,注解式开发的应用场景非常广泛,可以在各种类型的应用程序中使用,从而提高代码的可维护性和可扩展性。

@Cacheable

@Cacheable是Spring框架中的一个注解,用于将方法的返回值缓存起来,以提高应用程序的性能。当在相同参数下再次调用该方法时,会直接从缓存中获取结果,而不是重新执行一遍方法。

@Cacheable可以标注在类或方法上。当标注在类上时,表示该类的所有方法都可以被缓存。当标注在方法上时,表示该方法可以被缓存。

@Cacheable可以设置以下属性:

  • value:缓存的名称,必填项。
  • key:缓存的键值,可以使用SpEL表达式,缺省值为方法的所有参数。
  • condition:缓存的条件,可以使用SpEL表达式,缺省值为"true"。
  • unless:缓存的结果是否需要反向存储,可以使用SpEL表达式。

以下是一个使用@Cacheable注解的示例:

复制代码
@Cacheable(value = "myCache", key = "#id") public User getUserById(String id) { User user = userRepository.findById(id); return user; }

在这个示例中,getUserById方法的返回值会被缓存起来,并放入名称为"myCache"的缓存中,缓存的键值为方法的参数id。当再次调用该方法时,如果参数id相同,则直接从缓存中获取结果,而不是重新执行一遍方法。

@CachePut

@CachePut是Spring框架中的一个注解,用于更新缓存中的数据。与@Cacheable注解不同的是,@CachePut注解无论缓存中是否存在对应的数据,都会执行方法并将返回值更新到缓存中。

@CachePut可以标注在类或方法上。当标注在类上时,表示该类的所有方法都可以被更新缓存。当标注在方法上时,表示该方法可以更新缓存。

@CachePut可以设置以下属性:

  • value:缓存的名称,必填项。
  • key:缓存的键值,可以使用SpEL表达式,缺省值为方法的所有参数。
  • condition:缓存的条件,可以使用SpEL表达式,缺省值为"true"。
  • unless:缓存的结果是否需要反向存储,可以使用SpEL表达式。

以下是一个使用@CachePut注解的示例:

复制代码
@CachePut(value = "myCache", key = "#id") public User updateUserById(String id, User user) { userRepository.updateById(id, user); return user; }

在这个示例中,updateUserById方法会更新数据库中的数据,并将更新后的User对象放入名称为"myCache"的缓存中,缓存的键值为方法的参数id。无论缓存中是否存在对应的数据,都会执行该方法并更新缓存。

@CacheEvict

@CacheEvict是Spring框架中的一个注解,用于从缓存中删除数据。当我们更新或删除数据时,需要将缓存中的数据同步删除,避免脏数据的出现,这时可以使用@CacheEvict注解。

@CacheEvict可以标注在类或方法上。当标注在类上时,表示该类的所有方法都可以被删除缓存。当标注在方法上时,表示该方法可以删除缓存。

@CacheEvict可以设置以下属性:

  • value:缓存的名称,必填项。
  • key:缓存的键值,可以使用SpEL表达式,缺省值为方法的所有参数。
  • condition:删除缓存的条件,可以使用SpEL表达式,缺省值为"true"。
  • allEntries:是否删除缓存中的所有数据,缺省值为false。

以下是一个使用@CacheEvict注解的示例:

复制代码
@CacheEvict(value = "myCache", key = "#id") public void deleteUserById(String id) { userRepository.deleteById(id); }

在这个示例中,deleteUserById方法会从数据库中删除对应的数据,并将名称为"myCache"、键值为id的缓存数据删除。如果allEntries属性设置为true,则会删除"myCache"中所有的数据。

击穿、穿透、雪崩

这三个术语都是与缓存相关的问题。

击穿

击穿指的是一个非常热点的数据在缓存中失效,此时大量请求直接请求到数据库,导致数据库压力骤增,进而导致系统性能急剧下降。通常的解决方案是设置热点数据永远不过期,或者使用互斥锁(比如Redis中的SETNX),只允许一个线程去更新缓存。

常见的解决方案有两种:

互斥锁

逻辑过期

穿透

穿透则是指请求的数据在缓存中不存在,且数据库中也不存在,这样大量请求会直接透过缓存请求到数据库上,造成数据库压力增大,系统性能下降。为避免这种情况,可以在缓存中加入空对象,并设置一个较短的过期时间,这样请求队列中的请求在得到空对象后就不会再去请求数据库。

解决方案

缓存null值

布隆过滤

增强id的复杂度,避免被猜测id规律

做好数据的基础格式校验

加强用户权限校验

做好热点参数的限流

雪崩

雪崩是指缓存中的大量数据同时失效,导致一大批请求直接访问数据库,造成数据库瞬间压力过大,系统崩溃。为了避免这种情况,可以在缓存中设置不同的过期时间,使得不同的数据在不同的时间失效,从而减轻数据库的压力。同时,可以设置本地缓存或者分布式锁来避免缓存失效时大量请求同时访问数据库的情况。

解决方案:

给不同的Key的TTL添加随机值

利用Redis集群提高服务的可用性

给缓存业务添加降级限流策略

给业务添加多级缓存

相关推荐
咖啡八杯20 小时前
GoF设计模式——备忘录模式
java·后端·spring·设计模式
vivo互联网技术1 天前
从 10 分钟到 1 秒:ES 深度分页任意跳页的三轮优化实战
服务器·数据库·redis·elasticsearch·深度分页
Flittly2 天前
【AgentScope Java新手村系列】(16)从RAG到多路检索
java·spring boot·spring
咖啡八杯3 天前
GoF设计模式——中介者模式
java·后端·spring·设计模式
用户3074596982074 天前
Redis 延时队列详解
redis
烤代码的吐司君4 天前
Redis 数据结构 ZSet, BIT, HyperLogLog,Geo 空间数据
redis·后端
Flittly4 天前
【AgentScope Java新手村系列】(14)人机交互
java·spring boot·spring
leeyi6 天前
Checkpoint 机制:Agent 怎么在断电后接着跑
redis·aigc·agent
云技纵横7 天前
一个 @Async 让循环依赖暴雷:Spring 代理的暗坑
redis