文章目录
- 引言
- 1. Spring整合Redis
- 1.1. 为什么选择Redis作为缓存解决方案?
- Redis的特点和优势
- Redis与传统关系数据库的对比
- 1.2. Spring与Redis整合的基本步骤
- 2. Redis注解式缓存
- 2.1. Spring提供的缓存注解介绍
- 2.2. 使用注解实现方法级别的缓存
- 3. Redis的击穿、穿透和雪崩问题及解决方案
- 3.1. 几种常见的Redis缓存问题
- 3.2. 解决方案和优化策略
- 总结
引言
在现代应用开发中,缓存是提升性能和扩展性的关键技术之一。而Redis作为一种高性能、内存存储型数据库,与Spring框架的结合可以带来更加优雅和灵活的缓存解决方案。本篇博客将深入探讨Spring整合Redis的方法和最佳实践,并重点介绍Redis注解式缓存以及解决常见的Redis问题。
1. Spring整合Redis
1.1. 为什么选择Redis作为缓存解决方案?
Redis的特点和优势
-
内存存储:Redis将数据存储在内存中,因此具有非常高的读写速度。相比传统数据库的磁盘存储,Redis能够快速处理大量请求。
-
键值存储:Redis使用键值对(key-value)的数据结构来存储数据。这种简单的数据模型使得数据操作非常高效和灵活,适用于各种场景。
-
支持多种数据类型:Redis支持丰富的数据类型,包括字符串、哈希、列表、集合、有序集合等。这些数据类型的支持使得开发者可以更方便地构建复杂的应用逻辑。
-
缓存功能:Redis常被用作缓存服务器,可以将频繁访问的数据存储在内存中,从而加快数据读取速度。同时,Redis还提供了一些高级的缓存策略,如过期时间、LRU淘汰等。
-
发布订阅系统:Redis提供了发布订阅(pub/sub)功能,允许客户端通过订阅某个频道来接收消息的推送。这种消息发布订阅模式对于实时通信、消息队列等场景非常有用。
-
分布式支持:Redis支持分布式部署,可以将数据分布在多个节点上进行存储和访问。这种分布式的特性使得Redis具备高可用性和横向扩展的能力。
Redis与传统关系数据库的对比
-
数据模型:Redis采用键值对的数据模型,而传统关系数据库使用表格模型。这使得Redis在处理简单查询和高并发读写方面更加高效,而关系数据库更适合复杂的数据关联和查询操作。
-
存储方式:Redis将数据存储在内存中,而关系数据库通常将数据持久化到磁盘中。因此,Redis在读写速度方面更快,但关系数据库在数据持久化和容量方面具有优势。
-
功能特性:Redis提供了丰富的功能特性,如发布订阅、缓存策略等,而关系数据库更加注重数据一致性、事务管理以及复杂的查询能力。
-
扩展性:Redis支持分布式部署和横向扩展,可以通过添加新的节点来提高性能和容量。而关系数据库通常需要进行垂直扩展,即增加更强大的硬件来应对高负载。
-
数据一致性:Redis默认情况下是单机的,不保证数据的强一致性,而关系数据库通常会保证数据的一致性。
1.2. Spring与Redis整合的基本步骤
准备步骤:
- 打开redis服务
- 导入spring整合redis项目
- 当spring-context.xml文件需要注册多个.properties结尾的配置文件,不能在spring-*中添加注册
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>
redis.properties
redis.hostName=localhost
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
spring-redis.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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/cachehttp://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操作模板,使用该对象可以操作redishibernate课程中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>
- resources的配置文件的配置必须要涵盖读取.properties结尾的文件
- redisTemplate的使用,可以参照jdbcTemplate、amqpTemplate、rabbitMQtemplate等
2. Redis注解式缓存
2.1. Spring提供的缓存注解介绍
package com.zking.ssm.biz;import com.zking.ssm.model.Clazz;
import com.zking.ssm.util.PageBean;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;import java.util.List;
import java.util.Map;public interface ClazzBiz {@CacheEvict(value = "xx",key = "'cid:'+#cid",allEntries = true)int deleteByPrimaryKey(Integer cid);int insert(Clazz record);int insertSelective(Clazz record);// xx=cache-cid:1
// key的作用改变原有的key生成规则
// @Cacheable(value = "xx",key = "'cid:'+#cid",condition = "#cid > 6")@CachePut(value = "xx",key = "'cid:'+#cid",condition = "#cid > 6")Clazz selectByPrimaryKey(Integer cid);int updateByPrimaryKeySelective(Clazz record);int updateByPrimaryKey(Clazz record);List<Clazz> listPager(Clazz clazz, PageBean pageBean);List<Map> listMapPager(Clazz clazz, PageBean pageBean);
}
@Cacheable:缓存方法的返回值,可读写
@CachePut:更新缓存的方法,只写不读,适合读写分离
@CacheEvict:移除缓存中的数据
2.2. 使用注解实现方法级别的缓存
在Spring Boot项目中开启注解式缓存
定义缓存的Key和Value的生成策略
编写示例代码演示注解式缓存的使用方式
3. Redis的击穿、穿透和雪崩问题及解决方案
3.1. 几种常见的Redis缓存问题
击穿:大量请求同时查询一个不存在的Key,导致请求都落到数据库上
穿透:恶意请求查询一个不存在的Key,导致请求直接落到数据库上
雪崩:大量缓存失效导致所有请求都落到数据库上
3.2. 解决方案和优化策略
击穿解决方案:设置热点数据永不过期、互斥锁、布隆过滤器等
穿透解决方案:空值缓存、参数校验、布隆过滤器等
雪崩解决方案:限流、降级、异步更新缓存等
总结
在本篇博客中,我们深入探讨了Redis与Spring的整合以及注解式缓存的使用方式。通过学习和实践,我们了解了Redis作为一种高性能缓存解决方案的优势,并且掌握了基于Spring框架使用Redis的方法。