1、雪崩:
举个简单的例子:如果所有首页的Key失效时间都是12小时,中午12点刷新的,我零点有个秒杀活动大量用户涌入,假设当时每秒 6000 个请求,本来缓存在可以扛住每秒 5000 个请求,但是缓存当时所有的Key都失效了。此时 1 秒 6000 个请求全部落数据库,数据库必然扛不住,它会报一下警,真实情况可能DBA都没反应过来就直接挂了。此时,如果没用什么特别的方案来处理这个故障,DBA 很着急,重启数据库,但是数据库立马又被新的流量给打死了。这就是我理解的缓存雪崩。
同一时间大面积失效,那一瞬间Redis跟没有一样,那这个数量级别的请求直接打到数据库几乎是灾难性的,你想想如果打挂的是一个用户服务的库,那其他依赖他的库所有的接口几乎都会报错,如果没做熔断等策略基本上就是瞬间挂一片的节奏,你怎么重启用户都会把你打挂,等你能重启的时候,用户早就睡觉去了,并且对你的产品失去了信心,什么垃圾产品。
解决方案:
处理缓存雪崩简单,在批量往redis里存数据的时候,把每个key 的时效时间都加几个随机值就好了;这样可以保证数据不会在同一时间大面积失效。
setRedis(Key,value,time + Math.random() * 10000);
如果Redis是集群部署,将热点数据均匀分布在不同的Redis库中也能避免全部失效的问题,不过本渣我在生产环境中操作集群的时候,单个服务都是对应的单个Redis分片,是为了方便数据的管理,但是也同样有了可能会失效这样的弊端,失效时间随机是个好策略。
或者设置热点数据永远不过期,有更新操作就更新缓存就好了(比如运维更新了首页商品,那你刷下缓存就完事了,不要设置过期时间),电商首页的数据也可以用这个操作,保险。
2、穿透
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,我们数据库的id都是1开始自增上去的,如发起为id值为-1的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大,严重会击垮数据库。
如果后台不对参数做校验,数据库id都是大于0的,我一直都是用小于0的id去请求你,每次都能绕开redis直接打到数据库,数据库也查不到,每次都这样,并发高点就容易崩掉了。
解决方案:在接口层增加校验,比如用户鉴权校验,参数做校验,不合法的参数直接代码return;
从缓存取不到的数据,在数据库中也没有取到,这时也可以将对应的key-value写为null,稍后重试等提示。
这样可以防止攻击用户反复用同一个id暴力攻击,但是我们要知道正常用户是不会在单秒内发起这么多次请求的,可以让运维大大对单个ip每秒访问次数超过阈值的ip都拉黑。
3.缓存击穿
缓存击穿,这个跟缓存雪崩有点像,但是又有点不一样,缓存雪崩是因为大面积的缓存失效,打崩了DB,而缓存击穿不同的是,缓存击穿是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个完好无损的桶上凿开了一个洞。
解决方案:
设置热点数据永不过期,或者加上互斥锁就可以搞定。
- 布隆过滤器(Bloom Filter)
Bloom Filter,这个也能很好的防止缓存穿透的发生,他的原理也很简单就是利用高效的数据结构和算法快速判断出你这个key是否在数据库中存在,不存在直接return,存在你就去查DB,刷新kv再return;
5.一般避免以上情况发生我们从三个时间段去分析下:
*
事前:Redis 高可用,主从+哨兵,Redis cluster,避免全盘崩溃。
*
事中:本地 ehcache 缓存 + Hystrix 限流+降级,避免 MySQL 被打死。
*
事后:Redis 持久化 RDB+AOF,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
上面的几点我会在吊打系列Redis篇全部讲一下这个月应该可以吧Redis更完,限流组件,可以设置每秒的请求,有多少能通过组件,剩余的未通过的请求,怎么办?走降级!可以返回一些默认的值,或者友情提示,或者空白的值。
好处:
数据库绝对不会死,限流组件确保了每秒只有多少个请求能通过。 只要数据库不死,就是说,对用户来说,3/5 的请求都是可以被处理的。 只要有 3/5 的请求可以被处理,就意味着你的系统没死,对用户来说,可能就是点击几次刷不出来页面,但是多点几次,就可以刷出来一次。
这个在目前主流的互联网大厂里面是最常见的,你是不是好奇,某明星爆出什么事情,你发现你去微博怎么刷都空白界面,但是有的人又直接进了,你多刷几次也出来了,现在知道了吧,那是做了降级,牺牲部分用户的体验换来服务的安全,可还行?