艳艳耶✌️:个人主页
个人专栏 :《Spring与Mybatis集成整合》《Vue.js使用》
⛺️ 越努力 ,越幸运。
在Maven中添加Redis的依赖
2.9.0
1.7.1.RELEASE
redis.clients
jedis
${redis.version}
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-context.xml中需要注册多个properties文件,那么我们就不能够直接在spring-*.xml中添加注册,因为这样子的话,只能够读取一个配置文件,另一个配置文件会被覆盖掉,我们可以建一个文件用来专门引入外部文件
applicationContext
classpath:jdbc.properties
classpath:redis.properties
那么pom.xml中也需要进行修改,我们现在需要读取到所有的properties文件,所以需要是*.properties文件,而不能够指定是某一个配置文件
src/main/resources
*.properties
*.xml
首先需要一个缓冲策略类,用于储存信息
package com.sy.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;
}
}
Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果。
package com.sy.ssm.biz;
import com.sy.ssm.model.Clazz;
import com.sy.ssm.util.PageBean;
import org.springframework.cache.annotation.Cacheable;
import java.util.List;
import java.util.Map;
public interface ClazzBiz {
@Cacheable("clz")
Clazz selectByPrimaryKey(Integer cid);
}
运行效果:(运行两次)
Cacheable可以指定三个属性,value、key和condition。
我可定义key值来修改我们保存到redis缓冲的key值,并且可通过condition来制定什么时候需要缓冲,进一步优化性能。
自定义策略,如果查询的cid大于6才进行缓冲
package com.sy.ssm.biz;
import com.sy.ssm.model.Clazz;
import com.sy.ssm.util.PageBean;
import org.springframework.cache.annotation.Cacheable;
import java.util.List;
import java.util.Map;
public interface ClazzBiz {
@Cacheable(value = "clz",key = "'cid:'+#cid",condition = "#cid > 6")
Clazz selectByPrimaryKey(Integer cid);
}
它的使用与Cacheable的使用一致,它们的区别:
ppackage com.sy.ssm.biz;
import com.sy.ssm.model.Clazz;
import com.sy.ssm.util.PageBean;
import org.springframework.cache.annotation.Cacheable;
import java.util.List;
import java.util.Map;
public interface ClazzBiz {
@CachePut(value = "clz",key = "'cid:'+#cid",condition = "#cid > 6")
Clazz selectByPrimaryKey(Integer cid);
}
测试代码:
击穿指的是一个非常热门的数据在缓存中不存在,导致所有的请求都直接到达数据库,从而造成数据库负载过高,甚至可能引起系统崩溃。这种情况常常发生在缓存中设置了过期时间的数据,在数据失效的瞬间,有大量请求同时涌入,导致缓存无法命中并且每个请求都需要去访问数据库。
解决方案:
使用互斥锁机制:当一个请求发现缓存中不存在时,可以使用互斥锁机制来确保只有一个线程去查询数据库,其他线程等待查询结果。
提前异步加载:在缓存过期之前,提前异步加载数据到缓存,避免缓存过期时大量请求同时到达数据库
穿透指的是请求的数据在缓存和数据库中都不存在,这种情况通常是由于恶意请求或者非法请求导致的。这些请求绕过了缓存层直接访问数据库,造成了数据库的压力增加,资源浪费。
解决方案:
雪崩指的是缓存中大量的数据同时失效,导致所有请求都直接访问数据库,从而造成数据库负载激增,甚至导致系统崩溃。这种情况可能发生在缓存中的数据设置了相同的过期时间,当过期时间到达时,所有数据同时失效。
解决方案:
设置不同的过期时间:为不同的缓存数据设置稍有差异的过期时间,避免所有数据同时失效。
使用热点数据预加载:通过预先加载一些热门数据到缓存中,来降低缓存同时失效的风险。
分布式锁机制:在缓存数据失效时,使用分布式锁机制确保只有一个线程去重新加载缓存,其他线程等待缓存重新加载完成后再读取
解决方案:
其实上述的这三种问题,都有自己对应的解决方法,但是他们也有一个共同的方法可以解决--限流
在Redis中,限流是一种控制系统访问频率的机制,用于限制对某个资源或服务的并发访问数量,以防止系统过载或被恶意请求攻击。
限流的目的是通过限制请求的速率,保护系统的稳定性和可用性。它可以帮助平衡系统的负载,防止过多的请求同时涌入,导致系统不堪重负。
今日分享就结束呐