一般的缓存系统,都是按照key值去缓存查询,如果不存在对应的value,就应该去DB中查找 。
这个时候,如果请求的并发量很大,就会对后端的DB系统造成很大的压力。这就叫做缓存穿透。
关键词:缓存value为空;并发量很大去访问DB。
业务自身代码或数据出现问题;
一些恶意攻击、爬虫造成大量空的命中,此时会对数据库造成很大压力。
设置布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从避免了对底层存储系统的查询压力。
布隆过滤器: 判断一个元素是否在一个数组里面,如下图,利用二进制去做的一个存储,占用内存比较小,0 代表不存在,1 代表存在,添加查询效率很快,当保存了一个数值会经过一个算法将对应的值保存到布隆过滤器的集合上的某个位置,某个位置上可能会存在多个key,当传进来一个不存在的key值,和集合进行匹配,如果匹配不上便会返回一个null 布隆过滤器在redis集群的上一步,主要是判断数据是否存在于布隆过滤器中,这样减少对redis服务器的压力。布隆过滤器其原理就是把大数据量加载到内存中,判断数据是否存在。大数据量加载到内存中,这样容易导致OOM。一般是在应用启动的时候,进行初始化把数据加载到布隆过滤器中。布隆过滤器所需要占内存公式为:设bit数组大小为m,样本数量为n,失误率为p。由题可知 n = 500万,p = 3%(Google布隆过滤器默认为3%,我们也可以修改。 缺点: 1、1%的误判率,当有一个key不存在布隆数组中,但是由于这个误判率,在某个情况下回判断这个key存在,当这个数组越长误判率越低,数组越短误判率越高 2、当我们要删除某个key值的时候,是会删除我们的数据库和redis中的内容,但是布隆数组中无法删除,因为数组的某个位置上会存在对个key如果我们要删除的话就是将1变成0,但是会将其中所有的key值都删除 3.代码复杂度也会增加,因为我们要额外去维护一个集合,当我们使用redis集群,布隆过滤器要和redis结合在一起使用。
如果一个查询返回的数据为空,不管是数据不存在还是系统故障,我们仍然把这个结果进行缓存,但是它的过期时间会很短最长不超过5分钟。
if (list != null && list.size() > 0) { redisOperator.set("subCat:" + rootCatId, JsonUtils.objectToJson(list)); } else { redisOperator.set("subCat:" + rootCatId, JsonUtils.objectToJson(list), 5*60); }
因为缓存层承载了大量的请求,有效的保护了存储 层,但是如果缓存由于某些原因,整体不能够提供服务,于是所有的请求,就会到达存储层,存储层的调用量就会暴增,造成存储层也会挂掉的情况。缓存雪崩的英文解释是奔逃的野牛,指的是缓存层当掉之后,并发流量会像奔腾的野牛一样,大量后端存储。
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,大量数据会去直接访问DB,此时给DB很大的压力。
(1)设置redis集群和DB集群的高可用,如果redis出现宕机情况,可以立即由别的机器顶替上来。这样可以防止一部分的风险。
(2)使用互斥锁,在缓存失效后,通过加锁或者队列来控制读和写数据库的线程数量。比如:对某个key只允许一个线程查询数据和写缓存,其他线程等待。单机的话,可以使用synchronized或者lock来解决,如果是分布式环境,可以是用redis的setnx命令来解决。
(3)不同的key,可以设置不同的过期时间,让缓存失效的时间点不一致,尽量达到平均分布。
(4)永远不过期,Redis中设置永久不过期,这样就保证了,不会出现热点问题,也就是物理上不过期。
(5)资源保护,使用netflix的hystrix,可以做各种资源的线程池隔离,从而保护主线程池。