希望长大对我而言,是可以做更多想做的事,而不是被迫做更多不想做的事...... 首页 缓存穿透,缓存雪崩,缓存击穿 丁D 学无止境 2019-03-27 23:40 3815已阅读 缓存穿透 缓存雪崩 缓存击穿 摘要缓存穿透,缓存雪崩,缓存击穿 # 缓存穿透 缓存穿透是指查询一个一定不存在的数据,即**缓存和数据库中都没有**的数据。由于缓存不命中,并且出于容错考虑,如果从数据库查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,失去了缓存的意义。 比如findById 数据库的id是正数 findById?id=-1 查询一条id为-1的数据 ##如何解决缓存穿透 一:对查询不到的数据也做缓存处理,只是过期时间设置短一些! 二:我们可以使用**布隆过滤**器(BloomFilter)或者压缩filter提前拦截,不合法就不让这个请求到数据库层! # 缓存击穿 缓存击穿是指**缓存中没有但数据库中有**的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力 ##如何解决缓存击穿 一:设置热点数据**永远不过期**。 这里的**永远不过期**是指: (1) 从redis上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期。 (2) 从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期 从实战看,这种方法对于性能非常友好,唯一不足的就是构建缓存时候,其余线程(非构建缓存的线程)可能访问的是老数据,但是对于一般的互联网功能来说这个还是可以忍受。 ```java 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; } ``` 二:加**互斥锁**,互斥锁参考代码如下: ![alt](/upload/20180919143214879.png ) # 缓存雪崩 缓存雪崩是指缓存数据集中在同一个时间段过期,导致大量的请求跑到数据库去查询数据,造成mysql的压力过大,可能宕机。这种还不是最致命的,集中过期,理论上数据库也是能顶住压力的。 最致命的是当redis缓存服务器宕机导致,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮。 ##缓存雪崩后果 1、redis集群彻底崩溃 2、缓存服务大量对redis的请求hang住,占用资源 3、缓存服务大量的请求打到源头服务去查询mysql,直接打死mysql 4、源头服务因为mysql被打死也崩溃,对源服务的请求也hang住,占用资源 5、缓存服务大量的资源全部耗费在访问redis和源服务无果,最后自己被拖死,无法提供服务 6、nginx无法访问缓存服务,redis和源服务,只能基于本地缓存提供服务,但是缓存过期后,没有数据提供 ![alt](/upload/微信图片_20190722092219.png) ##如何解决缓存雪崩 分成事前,事中,事后三步骤 **事前** 一:错开设置过期时间(比如电商缓存商品可以对商品过期时间加一个随机因子,错开缓存过期时间) 发生缓存雪崩之前,事情之前,怎么去避免redis彻底挂掉 二:redis集群化高可用(哨兵,redis cluster,双机房部署,一部分几点部署在另一个机房) **事中** redis缓存服务器宕机,有大量请求无法访问redis了(以商品详情页多级缓存机构为例) 1. 还使用了ehcache缓存,应对零散的redis中数据被清除掉的现象,另外一个主要是预防redis彻底崩溃,ehcache的缓存还能支撑一阵 2. 对redis访问的资源隔离 3. 对源服务访问的限流以及资源隔离 **事后** 1. redis数据可以恢复,做了备份,redis数据备份和恢复,redis重新启动起来 2. redis数据彻底丢失了,或者数据过旧,快速缓存预热,redis重新启动起来 事中代码::对redis进行资源隔离 ```js com.netflix.hystrix hystrix-core 1.5.12 com.netflix.hystrix hystrix-metrics-event-stream 1.4.10 //构造函数传参数并,对同一类型分组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 { 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 { 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 很赞哦! (3) 上一篇:RocketMQ 下一篇:数据库主从不一致 目录 点击排行 Elasticsearch6.3.2之x-pack redis哨兵 2019-07-09 22:05 Redis+Twemproxy+HAProxy+Keepalived 2019-07-12 17:20 GC优化策略和相关实践案例 2019-10-10 10:54 JVM垃圾回收器 2019-10-10 10:23 标签云 Java Spring MVC Mybatis Ansible Elasticsearch Redis Hive Docker Kubernetes RocketMQ Jenkins Nginx 友情链接 郑晓博客 佛布朗斯基 凉风有信 MarkHoo's Blog 冰洛博客 南实博客 Rui | 丁D Java研发工程师 生活可以用「没办法」三个字概括。但别人的没办法是「腿长,没办法」、「长得好看,没办法」、「有才华,没办法」。而你的没办法,是真的没办法。 请作者喝咖啡