缓存雪崩
处理缓存雪崩在批量往**Redis**存数据的时候,把每个Key的失效时间都加个随机值就好了,这样可以保证数据不会在同一时间大面积失效,我相信,Redis这点流量还是顶得住的。
```java setRedis(Key,value,time + Math.random() * 10000);
如果**Redis**是集群部署,将热点数据均匀分布在不同的**Redis**库中也能避免全部失效的问题,不过本渣我在生产环境中操作集群的时候,单个服务都是对应的单个**Redis**分片,是为了方便数据的管理,但是也同样有了可能会失效这样的弊端,失效时间随机是个好策略。
或者设置热点数据永远不过期,有更新操作就更新缓存就好了(比如运维更新了首页商品,那你刷下缓存就完事了,不要设置过期时间),电商首页的数据也可以用这个操作,保险。
解决方法:
1、 使用快速失败的熔断策略,减少DB瞬间压力
2、 使用主从模式和集群模式来尽量保证缓存服务的高可用
缓存穿透
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求。
Eg: 我们数据库的 id 都是1开始自增上去的,如发起为id值为 -1 的数据或 id 为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大,严重会击垮数据库。
像这种你如果不对参数做校验,数据库id都是大于0的,我一直用小于0的参数去请求你,每次都能绕开Redis直接打到数据库,数据库也查不到,每次都这样,并发高点就容易崩掉了。
解决方案:缓存穿透 在接口层增加校验,比如用户鉴权校验,参数做校验,不合法的参数直接代码Return,比如:id 做基础校验,id <=0的直接拦截等。
举个简单的例子,你这个接口是分页查询的,但是你没对分页参数的大小做限制,调用的人万一一口气查 Integer.MAX_VALUE 一次请求就要你几秒,多几个并发你不就挂了么?是公司同事调用还好大不了发现了改掉,但是如果是黑客或者竞争对手呢?在你双十一当天就调你这个接口会发生什么,就不用我说了吧。这是之前的Leader跟我说的,我觉得大家也都应该了解下。**
从缓存取不到的数据,在数据库中也没有取到,这时也可以将对应Key的Value对写为null、位置错误、稍后重试这样的值具体取啥问产品,或者看具体的场景,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。
这样就可以防止攻击用户反复用同一个id暴力攻击。
另外高级算法
Redis**还有一个高级用法**布隆过滤器(Bloom Filter)**这个也能很好的防止缓存穿透的发生,他的原理也很简单就是利用高效的数据结构和算法快速判断出你这个Key是否在数据库中存在,不存在你return就好了,存在你就去查了DB刷新KV再return。
布隆过滤器
布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
用很小的空间,解决上述类似问题
实现原理:
当一个元素被加入集合时,通过k个散列函数将这个元素映射成一个位数组中的k个点,把它们置为1.检索时,我们只要看看这些点是不是都时1就(大约)知道集合中有没有它了:
如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在,这就是布隆过滤器的基本思想
一个很长的二机制向量和若干个哈希函数。
常用的几个应用场景:
1、 cerberus在收集监控数据的时候, 有的系统的监控项量会很大,需要检查一个监控项的名字是否已经被记录到db过了, 如果没有的话就需要写入db.
2、爬虫过滤已抓到的url就不再抓,可用bloom
filter过滤
3、垃圾邮件过滤。如果用哈希表,每存储一亿个email地址,就需要 1.6GB的内存(用哈希表实现的具体办法是将每一个 email地址对应成一个八字节的信息指纹,然后将这些信息指纹存入哈希表,由于哈希表的存储效率一般只有 50%,因此一个 email地址需要占用十六个字节。一亿个地址大约要 1.6GB,即十六亿字节的内存)。因此存贮几十亿个邮件地址可能需要上百 GB的内存。而Bloom Filter只需要哈希表 1/8到 1/4 的大小就能解决同样的问题。
缓存击穿
缓存击穿是指一个key非常热点,在不停的扛着大并发,大并发集中对这个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个完好无损的桶上凿开一个洞。
缓存击穿的解决办法是:设置热点数据永远不过期。或者加上互斥锁就能搞定
具体的解决方式:
1、 可以使用互斥锁更新,保证同一进程中针对同一各数据不会并发请求到DB,减小DB压力。
2、 使用随机退避方式,失效时随机sleep一个很短的时间,再次查询,如果失败再执行更新。
3、 针对多各热点key同时失效的问题,可以在缓存时使用固定时间加上一个小的随机数,
避免大量热点key同一时刻失效
一般避免以上情况发生我们从三个时间段去分析下:
-事前:**Redis** 高可用,主从+哨兵,**Redis
cluster**,避免全盘崩溃。
-事中:本地**ehcache** 缓存 + **Hystrix** 限流+降级,避免** MySQL** 被打死。
-事后:**Redis** 持久化**RDB**+**AOF**,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。