SSM之spring注解式缓存redis

本文涉及的产品
云数据库 Redis 版,标准版 2GB
推荐场景:
搭建游戏排行榜
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
简介: 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集群提高服务的可用性


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


给业务添加多级缓存

相关实践学习
基于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
目录
相关文章
|
22天前
|
编解码 NoSQL Java
使用Spring Boot + Redis 队列实现视频文件上传及FFmpeg转码的技术分享
【8月更文挑战第30天】在当前的互联网应用中,视频内容的处理与分发已成为不可或缺的一部分。对于视频平台而言,高效、稳定地处理用户上传的视频文件,并对其进行转码以适应不同设备的播放需求,是提升用户体验的关键。本文将围绕使用Spring Boot结合Redis队列技术来实现视频文件上传及FFmpeg转码的过程,分享一系列技术干货。
61 3
|
26天前
|
Java 开发者 Spring
【SpringBoot 异步魔法】@Async 注解:揭秘 SpringBoot 中异步方法的终极奥秘!
【8月更文挑战第25天】异步编程对于提升软件应用的性能至关重要,尤其是在高并发环境下。Spring Boot 通过 `@Async` 注解简化了异步方法的实现。本文详细介绍了 `@Async` 的基本用法及配置步骤,并提供了示例代码展示如何在 Spring Boot 项目中创建与管理异步任务,包括自定义线程池、使用 `CompletableFuture` 处理结果及异常情况,帮助开发者更好地理解和运用这一关键特性。
104 1
|
23天前
|
缓存 Java 数据库连接
Spring Boot奇迹时刻:@PostConstruct注解如何成为应用初始化的关键先生?
【8月更文挑战第29天】作为一名Java开发工程师,我一直对Spring Boot的便捷性和灵活性着迷。本文将深入探讨@PostConstruct注解在Spring Boot中的应用场景,展示其在资源加载、数据初始化及第三方库初始化等方面的作用。
44 0
|
7天前
|
Java 数据库连接 数据格式
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
IOC/DI配置管理DruidDataSource和properties、核心容器的创建、获取bean的方式、spring注解开发、注解开发管理第三方bean、Spring整合Mybatis和Junit
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
|
20天前
|
NoSQL Redis 缓存
Redis 加持下的 Spring 应用性能革命:见证毫秒级响应速度,打造极致用户体验!
【8月更文挑战第31天】Redis 是一个高性能键值存储系统,常用于数据库、缓存及消息中间件。与 Spring 框架集成后,可显著提升应用性能,特别是在高并发场景下。本文通过电子商务网站商品详情页的例子,展示了如何配置 Redis 并使用 `RedisTemplate` 进行数据操作,通过缓存策略优化应用性能,减轻数据库压力。例如,在 `ProductService` 类中,先从 Redis 获取商品信息,若未命中则从数据库获取并缓存至 Redis。此外,还介绍了如何通过 REST 控制器模拟 HTTP 请求进行测试。在实际项目中,结合 Spring Cache 注解可更便捷地管理缓存策略。
32 0
|
22天前
|
缓存 NoSQL Java
惊!Spring Boot遇上Redis,竟开启了一场缓存实战的革命!
【8月更文挑战第29天】在互联网时代,数据的高速读写至关重要。Spring Boot凭借简洁高效的特点广受开发者喜爱,而Redis作为高性能内存数据库,在缓存和消息队列领域表现出色。本文通过电商平台商品推荐系统的实战案例,详细介绍如何在Spring Boot项目中整合Redis,提升系统响应速度和用户体验。
44 0
|
22天前
|
监控 安全 Java
【开发者必备】Spring Boot中自定义注解与处理器的神奇魔力:一键解锁代码新高度!
【8月更文挑战第29天】本文介绍如何在Spring Boot中利用自定义注解与处理器增强应用功能。通过定义如`@CustomProcessor`注解并结合`BeanPostProcessor`实现特定逻辑处理,如业务逻辑封装、配置管理及元数据分析等,从而提升代码整洁度与可维护性。文章详细展示了从注解定义、处理器编写到实际应用的具体步骤,并提供了实战案例,帮助开发者更好地理解和运用这一强大特性,以实现代码的高效组织与优化。
34 0
|
4月前
|
存储 缓存 Java
【Spring原理高级进阶】有Redis为啥不用?深入剖析 Spring Cache:缓存的工作原理、缓存注解的使用方法与最佳实践
【Spring原理高级进阶】有Redis为啥不用?深入剖析 Spring Cache:缓存的工作原理、缓存注解的使用方法与最佳实践
|
4月前
|
缓存 Java 数据库
优化您的Spring应用程序:缓存注解的精要指南
优化您的Spring应用程序:缓存注解的精要指南
93 0
|
4月前
|
缓存 NoSQL Java
Spring Cache之本地缓存注解@Cacheable,@CachePut,@CacheEvict使用
SpringCache不支持灵活的缓存时间和集群,适合数据量小的单机服务或对一致性要求不高的场景。`@EnableCaching`启用缓存。`@Cacheable`用于缓存方法返回值,`value`指定缓存名称,`key`定义缓存键,可按SpEL编写,`unless`决定是否不缓存空值。当在类上使用时,类内所有方法都支持缓存。`@CachePut`每次执行方法后都会更新缓存,而`@CacheEvict`用于清除缓存,支持按键清除或全部清除。Spring Cache结合Redis可支持集群环境。
239 6