目录
一.redis整合
1.1.pom配置
1.2.配置文件spring-redis.xml
1.3.配置redis的key生成策略
二.redis的注解式开发及应用场景
2.1.什么是redis的注解式
2.2.@Cacheable
2.3.自定义策略
2.4.CachePut 注解
三.redis的击穿穿透雪崩
1.击穿问题
2.穿透问题
3.雪崩问题
注1:当spring-context.xml中需要注册多个.properties,那么
不能在spering-*.xml中添加注册
注2:resources的配置必须要涵盖读取.preperties 结尾的文件
注3:redisTemplate的使用,可以参照jdbcTemplate,amqpTemplate,rabbitMQtemplate
4.0.0
org.example
ssm2
1.0-SNAPSHOT
war
ssm2 Maven Webapp
http://www.example.com
UTF-8
1.8
1.8
3.7.0
5.0.2.RELEASE
3.4.5
5.1.44
5.1.2
1.3.1
2.1.1
2.4.3
2.9.1
4.12
4.0.0
1.18.2
2.10.0
1.7.7
2.9.0
1.7.1.RELEASE
org.springframework
spring-context
${spring.version}
org.springframework
spring-orm
${spring.version}
org.springframework
spring-tx
${spring.version}
org.springframework
spring-aspects
${spring.version}
org.springframework
spring-web
${spring.version}
org.springframework
spring-test
${spring.version}
org.mybatis
mybatis
${mybatis.version}
mysql
mysql-connector-java
${mysql.version}
com.github.pagehelper
pagehelper
${pagehelper.version}
org.mybatis
mybatis-spring
${mybatis.spring.version}
org.apache.commons
commons-dbcp2
${commons.dbcp2.version}
org.apache.commons
commons-pool2
${commons.pool2.version}
org.apache.logging.log4j
log4j-core
${log4j2.version}
org.apache.logging.log4j
log4j-api
${log4j2.version}
org.apache.logging.log4j
log4j-web
${log4j2.version}
junit
junit
${junit.version}
test
javax.servlet
javax.servlet-api
${servlet.version}
provided
org.projectlombok
lombok
${lombok.version}
provided
org.springframework
spring-webmvc
${spring.version}
javax.servlet.jsp
javax.servlet.jsp-api
2.3.3
jstl
jstl
1.2
taglibs
standard
1.1.2
commons-fileupload
commons-fileupload
1.3.3
org.hibernate
hibernate-validator
6.0.7.Final
com.fasterxml.jackson.core
jackson-databind
2.9.3
com.fasterxml.jackson.core
jackson-core
2.9.3
com.fasterxml.jackson.core
jackson-annotations
2.9.3
org.apache.shiro
shiro-core
1.3.2
org.apache.shiro
shiro-web
1.3.2
org.apache.shiro
shiro-spring
1.3.2
net.sf.ehcache
ehcache
${ehcache.version}
org.slf4j
slf4j-api
${slf4j-api.version}
org.slf4j
jcl-over-slf4j
${slf4j-api.version}
runtime
org.apache.logging.log4j
log4j-slf4j-impl
${log4j2.version}
redis.clients
jedis
${redis.version}
org.springframework.data
spring-data-redis
${redis.spring.version}
ssm2
src/main/java
**/*.xml
src/main/resources
*.properties
*.xml
org.apache.maven.plugins
maven-compiler-plugin
${maven.compiler.plugin.version}
${maven.compiler.target}
${project.build.sourceEncoding}
org.mybatis.generator
mybatis-generator-maven-plugin
1.3.2
mysql
mysql-connector-java
${mysql.version}
true
maven-clean-plugin
3.1.0
maven-resources-plugin
3.0.2
maven-compiler-plugin
3.8.0
maven-surefire-plugin
2.22.1
maven-war-plugin
3.2.2
maven-install-plugin
2.5.2
maven-deploy-plugin
2.8.2
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
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;
}
}
@Cacheable 配置在方法或类上,作用:本方法执行后,先去缓存看有没有数据,如果没有,从数据库中查找出来,给缓存中存一份,返回结果, 下次本方法执行,在缓存未过期情况下,先在缓存中查找,有的话直接返回,没有的话从数据库查找
@CachePut 类似于更新操作,即每次不管缓存中有没有结果,都从数据库查找结果,并将结果更新到缓存,并返回结果
@CacheEvict 用来清除用在本方法或者类上的缓存数据(用在哪里清除哪里)
定义查询接口使用Cacheable注解
编写测试类
测试结果
@Cacheable可以指定三个属性,value、key和condition。
它的使用与Cacheable的使用一致,它们的区别:
- Cacheable:会在redis中存储数据,同时也会读取数据
- CachePut:只会在redis储存数据,不会进行读取操作
测试类
测试结果
当一个在缓存中不存在但是经常查询的 key 被请求时,大量的请求会穿透缓存直接到达数据库,导致数据库压力过大,影响系统性能。
解决方法:
使用布隆过滤器,将所有可能出现的查询 key 存入布隆过滤器中,如果布隆过滤器返回不存在该 key,则直接返回不存在即可,避免了对底层存储系统的查询操作,减小了数据库的压力。
当存在大量的恶意请求或者访问不存在的 key 时,这些请求会穿透缓存直接到达底层存储系统,导致底层存储系统压力过大,甚至会导致底层存储系统崩溃。
解决方法:
也是使用布隆过滤器,在布隆过滤器返回不存在该 key 时,不再直接查询底层存储系统,而是将请求拦截。
当 Redis 缓存中大量的数据过期后,同时有大量请求访问这些已经过期的数据时,这些请求会穿透 Redis 直接到达底层存储系统,导致底层存储系统压力过大,甚至崩溃。
解决方法:
使用分布式锁和缓存预热。对于 Redis 中的数据,定期进行缓存预热,提高缓存命中率;同时对于热点数据,可以使用分布式锁,保证在数据过期时,只有一个请求去查询底层存储系统,并在查询完数据后重新设置缓存。