缓存雪崩和缓存穿透的现象和解决方案+boomfilter的引入
缓存雪崩和缓存穿透的现象和解决方案
1.什么是缓存雪崩
由于各种原因,redis集群整体宕机,缓存不可用,导致所有的请求都直接发到数据库,数据库抗不了这种高并发直接挂掉,数据库挂掉,就可能引发整体系统不可用.
2.缓存雪崩的解决方案
事前:redis做好高可用架构
事中:开启本地ehcache缓存+hystrix/sentinal熔断 限流 降级的保护机制,保证数据库不会被请求搞挂机,一个请求顺序为:先查ehcache->redis->hystrix/sentinal保护机制->mysql
事后:redis开启持久化机制(RDB/AOF),保证缓存挂了可以根据磁盘文件快速恢复
3.什么是缓存穿透
外来的黑客攻击,发起大量数据库没有的id进行查询,这样既然数据库都没有,缓存肯定没有,就是直接穿过缓存查询数据库,大量请求过来也会把数据库搞挂机
4.缓存穿透的解决方案
很简单,缓存空值,如果数据库没查到的值,直接根据id在缓存中设置一个NUKNOW的值,这样下次请求就直接走缓存.
但是这样也会出现一个问题,缓存会存储大量的空值,以及数据查询还会走数据库,可以在加一层BoomFilter(布隆过滤器),具体原理就是先把数据库的数据装载一份到boomfilter,查询的时候先判断redis中该值在不在,如果不在就通过boomfilter进行判断是否在数据库中,判断也没有就直接返回空值.这样可以避免大量的缓存空值,也能有效避免黑客攻击,不好的地方就是:
代码复杂度增大,
需要另外维护一个集合来存放缓存的Key,
布隆过滤器不支持删值操作
布隆过滤器有一定的误判率,想要误判率越小,那么所需的空间越大
不过正确的数据都是可以通过的,对于不存在的key,可能会出现误判,就是返回也存在数据库的情况,这个可以根据实际情况进行调整误判的概率;
附boomfilter的示例代码:
引入依赖
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>23.0</version> </dependency>
import com.google.common.hash.BloomFilter; import com.google.common.hash.Funnels; import java.math.BigDecimal; public class BloomFilterTest { private static int size = 1000000; //fpp代表误判率控制在多少 private static BloomFilter<Integer> bf = BloomFilter.create(Funnels.integerFunnel(), size,0.01); public static void main(String[] args) { // 初始化1000000条数据到过滤器中 for (int i = 0; i < size; i++) { bf.put(i); } int failCount=0; // 匹配已在过滤器中的值,是否有匹配不上的 for (int i = 0; i < size; i++) { if (!bf.mightContain(i)) { System.out.println("未匹配上的数据"); failCount++; } } System.out.println("未匹配上的正确数据量:"+failCount); // 匹配不在过滤器中的10000个值,有多少匹配出来 int count = 0; for (int i = size; i < size + 10000; i++) { if (bf.mightContain(i)) { count++; } } BigDecimal bigDecimal = new BigDecimal(count); BigDecimal divide = bigDecimal.divide(new BigDecimal(10000), 5, BigDecimal.ROUND_HALF_UP); System.out.println("误命中的数量:" + count+",误命中的概率:"+divide); } }