Redis缓存雪崩和穿透乍一看好像差不多,概念容易混淆。而解决方案的思路是让失效时间达到均匀的情况。
缓存雪崩是指在我们设置缓存失效时间上时采用了相同的过期时间,导致缓存在某一时刻同时失效。请求全部打到后端数据库,数据库一时请求过大,数据库cpu和IO一时负载过大,造成雪崩。如果不能理解的话,闭上眼睛想象一下,雪山崩塌的场景。
业界有常见的解决方案,没有哪一种方案是完美的,需要结合实际的并发情况选择最合适的方案。
第一种是加锁排队,其本质是一种缓冲方案,允许等待。弊端是从用户角度来说体验不是很好。
第二种是mutex互斥锁,比如Redis分布式锁SetNX。如果缓存里面没有并不是去加载数据库。
第三种是限流,在大流量来时缩紧。或者使用令牌桶,漏桶算法。
第四种是二级缓存,比如在缓存服务的基础上加上本地缓存,在缓存服务失效的情况下查询本地缓存而不是数据库。
Redis穿透是大量的请求在缓存没有命中,比如每次都查询一个不存在的值。导致每次都要去数据库查询,这样导致缓存被穿透了,从而增加数据库的压力。
Bloom filter,布隆过滤器实际上是一个很长的二进制向量和一系列随机映射函数。
我们知道Redis 之所以快是因为单线程避免了CPU上下文切换。并且采用了epoll 机制,还有一个重要的原因就是他的存储结构,时间复杂度是o(1)。为了减少存储空间Bloom Filter 是一种改善方案。
但是使用起来一定要谨慎,Bloom Filter 存在一定的概率误判,我们在学术上成为假阳性。随着元素的增加这种误判的几率会随之增加,但是一般情况下可以忽略。
缓存这些空对象,但是注意失效时间设置小一点,防止Redis的key过多。
在Guava中的实现,funnel:输入的数据,xpectedInsertions:预估计插入的元素总量,fpp:你自己想达到的误判率,strategy:实现的实例。
static <T> BloomFilter<T> create( Funnel<? super T> funnel, long expectedInsertions, double fpp, Strategy strategy) { checkNotNull(funnel); checkArgument( expectedInsertions >= 0, "Expected insertions (%s) must be >= 0", expectedInsertions); checkArgument(fpp > 0.0, "False positive probability (%s) must be > 0.0", fpp); checkArgument(fpp < 1.0, "False positive probability (%s) must be < 1.0", fpp); checkNotNull(strategy); if (expectedInsertions == 0) { expectedInsertions = 1; } /* * TODO(user): Put a warning in the javadoc about tiny fpp values, since the resulting size * is proportional to -log(p), but there is not much of a point after all, e.g. * optimalM(1000, 0.0000000000000001) = 76680 which is less than 10kb. Who cares! */ long numBits = optimalNumOfBits(expectedInsertions, fpp); int numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, numBits); try { return new BloomFilter<T>(new BitArray(numBits), numHashFunctions, funnel, strategy); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Could not create BloomFilter of " + numBits + " bits", e); }
缓存预热就是系统发布之前,先把缓存数据加载到系统里面,这样避免了活动正式开始之前首次没有命中,需要查询数据库的问题。
另外一方面预热也是提前对流量的一种预估方式,很多大型活动或者秒杀,都会提前来一波预热。
当并发量很大的时候,响应慢或者超时,影响到核心业务处理(比如下单,支付),或者导致到上游的系统的系统扇出,这种情形下需要对服务进行降级,也就是丢车保帅的做法。
级,也就是丢车保帅的做法。