首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >缓存穿透,缓存雪崩,缓存击穿

缓存穿透,缓存雪崩,缓存击穿

作者头像
丁D
发布2022-08-12 21:14:34
发布2022-08-12 21:14:34
1.8K00
代码可运行
举报
文章被收录于专栏:老铁丁D老铁丁D
运行总次数:0
代码可运行

缓存穿透

缓存穿透是指查询一个一定不存在的数据,即缓存和数据库中都没有的数据。由于缓存不命中,并且出于容错考虑,如果从数据库查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,失去了缓存的意义。

比如findById 数据库的id是正数 findById?id=-1 查询一条id为-1的数据

如何解决缓存穿透

一:对查询不到的数据也做缓存处理,只是过期时间设置短一些! 二:我们可以使用布隆过滤器(BloomFilter)或者压缩filter提前拦截,不合法就不让这个请求到数据库层!

缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力

如何解决缓存击穿

一:设置热点数据永远不过期。 这里的永远不过期是指: (1) 从redis上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期。 (2) 从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期

从实战看,这种方法对于性能非常友好,唯一不足的就是构建缓存时候,其余线程(非构建缓存的线程)可能访问的是老数据,但是对于一般的互联网功能来说这个还是可以忍受。

代码语言:javascript
代码运行次数:0
运行
复制
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了(以商品详情页多级缓存机构为例)

  1. 还使用了ehcache缓存,应对零散的redis中数据被清除掉的现象,另外一个主要是预防redis彻底崩溃,ehcache的缓存还能支撑一阵
  2. 对redis访问的资源隔离
  3. 对源服务访问的限流以及资源隔离

事后

  1. redis数据可以恢复,做了备份,redis数据备份和恢复,redis重新启动起来
  2. redis数据彻底丢失了,或者数据过旧,快速缓存预热,redis重新启动起来

事中代码::对redis进行资源隔离

代码语言:javascript
代码运行次数:0
运行
复制
<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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-03-27 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 缓存穿透
    • 如何解决缓存穿透
  • 缓存击穿
    • 如何解决缓存击穿
  • 缓存雪崩
    • 缓存雪崩后果
    • 如何解决缓存雪崩
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档