免费国外网站,福建住房和建设网站,网络营销都有哪些方法,中国无线网络公司排名前言
Redis缓存异常问题分别是#xff1a;1.缓存雪崩。2.缓存预热。3.缓存穿透。4.缓存降级。5.缓存击穿#xff0c;以
及对应Redis缓存异常问题解决方案。
1.缓存雪崩
1.1、什么是缓存雪崩
如果缓存集中在一段时间内失效#xff0c;发生大量的缓存穿透#xff0c;所有…前言
Redis缓存异常问题分别是1.缓存雪崩。2.缓存预热。3.缓存穿透。4.缓存降级。5.缓存击穿以
及对应Redis缓存异常问题解决方案。
1.缓存雪崩
1.1、什么是缓存雪崩
如果缓存集中在一段时间内失效发生大量的缓存穿透所有的查询都落在数据库上造成了缓存雪崩由于原有缓存失效新缓存未到期间所有原本应该访问缓存的请求都去查询数据库了而对数据库CPU和内存造成巨大压力严重的会造成数据库宕机。
举例来说 我们在准备一项抢购的促销运营活动活动期间将带来大量的商品信息、库存等相关信息的查询。
为了避免商品数据库的压力将商品数据放入缓存中存储。不巧的是抢购活动期间大量的热门商品缓存同时失
效过期了导致很大的查询流量落到了数据库之上对于数据库来说造成很大的压力。
1.2、解决方案
1、加锁排队
mutex互斥锁解决Redis的SETNX去set一个mutex key当操作返回成功时再进行加载数据库的操作并回设缓存否则就重试整个get缓存的方法。
2、数据预热
缓存预热就是系统上线后将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候先查询数据库然后再将数据缓存的问题。用户直接查询事先被预热的缓存数据。可以通过缓存reload机制预先去更新缓存再即将发生大并发访问前手动触发加载缓存不同的key。
3、双层缓存策略
C1为原始缓存C2为拷贝缓存C1失效时可以访问C2C1缓存失效时间设置为短期C2设置为长期。
4、定时更新缓存策略
实效性要求不高的缓存容器启动初始化加载采用定时任务更新或移除缓存。
5、设置不同的过期时间。
让缓存失效的时间点尽量均匀。
2.缓存预热
2.1、什么是缓存预热
缓存预热就是系统上线后将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候先查询数据库然后再将数据缓存的问题。用户直接查询事先被预热的缓存数据。
如图所示 如果不进行预热 那么 Redis 初识状态数据为空系统上线初期对于高并发的流量都会访问到数据库中 对数据库造成流量的压力。
2.2、解决方案 数据量不大的时候工程启动的时候进行加载缓存动作 数据量大的时候设置一个定时任务脚本进行缓存的刷新 数据量太大的时候优先保证热点数据进行提前加载到缓存。
3.缓存穿透
3.1、什么是缓存穿透
缓存穿透是指用户查询数据在数据库没有自然在缓存中也不会有。这样就导致用户查询的时候在缓存中找不到对应key的value每次都要去数据库再查询一遍然后返回空相当于进行了两次无用的查询。这样请求就绕过缓存直接查数据库。
3.2、解决方案
1、缓存空对象
简单粗暴的方法如果一个查询返回的数据为空不管是数据不存在还是系统故障我们仍然把这个空结果进行缓存但它的过期时间会很短最长不超过五分钟。
2、布隆过滤器
优势占用内存空间很小位存储性能特别高使用key的hash判断key存不存在。
将所有可能存在的数据哈希到一个足够大的bitmap中一个一定不存在的数据会被这个bitmap拦截掉从而避免了对底层存储系统的查询压力。
4.缓存降级
降级的情况就是缓存失效或者缓存服务挂掉的情况下我们也不去访问数据库。
我们直接访问内存部分数据缓存或者直接返回默认数据。
举例来说 对于应用的首页一般是访问量非常大的地方首页里面往往包含了部分推荐商品的展示信息。这些推荐商品都会放到缓存中进行存储同时我们为了避免缓存的异常情况对热点商品数据也存储到了内存中。同时内存中还保留了一些默认的商品信息。 降级一般是有损的操作所以尽量减少降级对于业务的影响程度。
5.缓存击穿
5.1、什么是缓存击穿
缓存击穿是指缓存中没有但数据库中有的数据一般是缓存时间到期这时由于并发用户特别多同时读缓存没
读到数据又同时去数据库去取数据引起数据库压力瞬间增大造成过大压力。
5.2、会带来什么问题
会造成某一时刻数据库请求量过大压力剧增。
5.3、解决方案
5.3.1.使用互斥锁(mutex key)
这种解决方案思路比较简单就是只让一个线程构建缓存其他线程等待构建缓存的线程执行完重新从缓存获取数据就可以了。
如果是单机可以用synchronized或者lock来处理
如果是分布式环境可以用分布式锁就可以了分布式锁可以用memcache的add, redis的setnx, zookeeper的添加节点操作。 5.3.2.永远不过期 从redis上看确实没有设置过期时间这就保证了不会出现热点key过期问题也就是“物理”不过期。 从功能上看如果不过期那不就成静态的了吗所以我们把过期时间存在key对应的value里如果发现要过期了通过一个后台的异步线程进行缓存的构建也就是“逻辑”过期。 5.3.3.缓存屏障
该方法类似于方法一
使用countDownLatch和atomicInteger.compareAndSet()方法实现轻量级锁。 public class MyCache{private ConcurrentHashMapString, String map;private CountDownLatch countDownLatch;private AtomicInteger atomicInteger;public MyCache(ConcurrentHashMapString, String map, CountDownLatch countDownLatch,AtomicInteger atomicInteger) {this.map map;this.countDownLatch countDownLatch;this.atomicInteger atomicInteger;}public String get(String key){String value map.get(key);if (value ! null){System.out.println(Thread.currentThread().getName()\t 线程获取value值 valuevalue);return value;}// 如果没获取到值// 首先尝试获取token然后去查询db初始化化缓存// 如果没有获取到token超时等待if (atomicInteger.compareAndSet(0,1)){System.out.println(Thread.currentThread().getName()\t 线程获取token);return null;}// 其他线程超时等待try {System.out.println(Thread.currentThread().getName()\t 线程没有获取token等待中。。。);countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}// 初始化缓存成功等待线程被唤醒// 等待线程等待超时自动唤醒System.out.println(Thread.currentThread().getName()\t 线程被唤醒获取value map.get(key));return map.get(key);}public void put(String key, String value){try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}map.put(key, value);// 更新状态atomicInteger.compareAndSet(1, 2);// 通知其他线程countDownLatch.countDown();System.out.println();System.out.println(Thread.currentThread().getName()\t 线程初始化缓存成功value map.get(key));}}public class MyThread implements Runnable{private MyCache myCache;public MyThread(MyCache myCache) {this.myCache myCache;}Overridepublic void run() {String value myCache.get(key);if (value null){myCache.put(key,value);}}}public class CountDownLatchDemo {public static void main(String[] args) {MyCache myCache new MyCache(new ConcurrentHashMap(), new CountDownLatch(1), new AtomicInteger(0));MyThread myThread new MyThread(myCache);ExecutorService executorService Executors.newFixedThreadPool(5);for (int i 0; i 5; i) {executorService.execute(myThread);}}}