缓存穿透是指查询一个一定不存在的数据,即缓存和数据库中都没有的数据。由于缓存不命中,并且出于容错考虑,如果从数据库查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,失去了缓存的意义。
比如findById 数据库的id是正数 findById?id=-1 查询一条id为-1的数据
一:对查询不到的数据也做缓存处理,只是过期时间设置短一些! 二:我们可以使用布隆过滤器(BloomFilter)或者压缩filter提前拦截,不合法就不让这个请求到数据库层!
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
一:设置热点数据永远不过期。 这里的永远不过期是指: (1) 从redis上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期。 (2) 从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期
从实战看,这种方法对于性能非常友好,唯一不足的就是构建缓存时候,其余线程(非构建缓存的线程)可能访问的是老数据,但是对于一般的互联网功能来说这个还是可以忍受。
String get(final String key) {
V v = redis.get(key);
String value = v.getValue();
long timeout = v.getTimeout();
if (v.timeout <= System.currentTimeMillis()) {
// 异步更新后台异常执行
threadPool.execute(new Runnable() {
public void run() {
String keyMutex = "mutex:" + key;
if (redis.setnx(keyMutex, "1")) {
// 3 min timeout to avoid mutex holder crash
redis.expire(keyMutex, 3 * 60);
String dbValue = db.get(key);
redis.set(key, dbValue);
redis.delete(keyMutex);
}
}
});
}
return value;
}
二:加互斥锁,互斥锁参考代码如下:
缓存雪崩是指缓存数据集中在同一个时间段过期,导致大量的请求跑到数据库去查询数据,造成mysql的压力过大,可能宕机。这种还不是最致命的,集中过期,理论上数据库也是能顶住压力的。
最致命的是当redis缓存服务器宕机导致,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮。
1、redis集群彻底崩溃
2、缓存服务大量对redis的请求hang住,占用资源
3、缓存服务大量的请求打到源头服务去查询mysql,直接打死mysql
4、源头服务因为mysql被打死也崩溃,对源服务的请求也hang住,占用资源
5、缓存服务大量的资源全部耗费在访问redis和源服务无果,最后自己被拖死,无法提供服务
6、nginx无法访问缓存服务,redis和源服务,只能基于本地缓存提供服务,但是缓存过期后,没有数据提供
分成事前,事中,事后三步骤 事前 一:错开设置过期时间(比如电商缓存商品可以对商品过期时间加一个随机因子,错开缓存过期时间) 发生缓存雪崩之前,事情之前,怎么去避免redis彻底挂掉 二:redis集群化高可用(哨兵,redis cluster,双机房部署,一部分几点部署在另一个机房)
事中 redis缓存服务器宕机,有大量请求无法访问redis了(以商品详情页多级缓存机构为例)
事后
事中代码::对redis进行资源隔离
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>1.5.12</version>
</dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-metrics-event-stream</artifactId>
<version>1.4.10</version>
</dependency>
//构造函数传参数并,对同一类型分组RedisGroup
import redis.clients.jedis.JedisCluster;
import com.alibaba.fastjson.JSONObject;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.roncoo.eshop.cache.model.ProductInfo;
import com.roncoo.eshop.cache.spring.SpringContext;
//获取返回null
public class GetProductInfoFromReidsCacheCommand extends HystrixCommand<ProductInfo> {
private Long productId;
public GetProductInfoFromReidsCacheCommand(Long productId) {
super(HystrixCommandGroupKey.Factory.asKey("RedisGroup"));
this.productId = productId;
}
@Override
protected ProductInfo run() throws Exception {
JedisCluster jedisCluster = (JedisCluster) SpringContext.getApplicationContext()
.getBean("JedisClusterFactory");
String key = "product_info_" + productId;
String json = jedisCluster.get(key);
if(json != null) {
return JSONObject.parseObject(json, ProductInfo.class);
}
return null;
}
}
//保存 返回Boolean
public class SaveProductInfo2ReidsCacheCommand extends HystrixCommand<Boolean> {
private ProductInfo productInfo;
public SaveProductInfo2ReidsCacheCommand(ProductInfo productInfo) {
super(HystrixCommandGroupKey.Factory.asKey("RedisGroup"));
this.productInfo = productInfo;
}
@Override
protected Boolean run() throws Exception {
JedisCluster jedisCluster = (JedisCluster) SpringContext.getApplicationContext()
.getBean("JedisClusterFactory");
String key = "product_info_" + productInfo.getId();
jedisCluster.set(key, JSONObject.toJSONString(productInfo));
return true;
}
}
/**
* 将商品信息保存到redis中
* @param productInfo
*/
public void saveProductInfo2ReidsCache(ProductInfo productInfo) {
SaveProductInfo2ReidsCacheCommand command = new SaveProductInfo2ReidsCacheCommand(productInfo);
command.execute();
}
https://www.cnblogs.com/leeSmall/articles/8594542.html
参考 https://blog.csdn.net/kongtiao5/article/details/82771694 https://zhuanlan.zhihu.com/p/59945689 https://www.jianshu.com/p/55e245bad12a https://www.cnblogs.com/dream-to-pku/p/9153999.html