首先声明下,该博客摘抄自敖丙巨佬,点击进入原文!
缓存雪崩
什么是缓存雪崩
首先说一下当前主流的系统架构
目前电商平台首页以及热点数据都会去做缓存,一般缓存都是定时任务去刷新,或者是查不到之后去更新的,但是定时刷新就会有一个问题。
举个例子:
如果所有首页的key失效时间都是12小时,每天中午12点刷新。但是某一天的零点有一个秒杀活动,便会有大量的用户涌入,假设当是每秒6000个请求,本来缓存存在时可以扛住每秒5000次的请求,但是此时所有的key都失效了。此时每秒6000个请求全部由数据库处理,数据库必然扛不住,所以数据库会报警,真实情况可能DBA都没反应过来就直接挂了。此时,如果没有什么特别的方案处理这个故障,DBA会重启数据库,但是数据库立马又被新的流量打死了。这就是缓存雪崩。
同一时间Redis中的key大面积失效,那一瞬间Redis就和没有一样,那么这个数量级的请求直接请求数据库的话结果几乎是灾难性的。若挂掉的数据库是处理用户服务的,那么其它所有依赖这个库的接口就全部瘫痪了,用户的体验感会急剧下降。
如何解决缓存雪崩
处理缓存雪崩的方法有很多
-
在批量往Redis存数据的时候,把每个key的失效时间都加一个随机的值就好了,这样可以保证数Redis中的数据不会在同一时刻大面积失效。
setRedis (key, value, time + Math.random() * 10000); -
如果Redis是集群部署,将热点数据均匀的分布在不同的Redis库中也能避免全部失效的问题。
-
或者设置热点数据永远不会过期,当有更新操作时更新缓存就好了(比如运维更新了首页商品,这是将缓存中的数据刷新一下就可以了,不要设置过期时间),电商类平台首页的数据也可以用这个操作,比较保险。
缓存穿透和击穿以及与雪崩的区别
什么是缓存穿透
缓存穿透视之缓存和数据库中都没有数据,而用户不断的发起请求,我们的数据库的id都是从1开始自增上去的,如果发起id为-1的数据或者id为特别大又不存在的数据,这时用户可能可能是攻击者,公鸡会导致数据库压力过大,甚至会使数据库瘫痪。
像这样如果不对用户传入的参数做校验,数据库的id都是大于0的,我一直用小于0的参数去请求,每次都能绕开Redis直接访问数据库,数据库也查不到,每次都这样,当并发量很高时数据库就容易崩溃掉。
什么事缓存击穿
缓存击穿和缓存雪崩很像,但是又有不一样的地方,缓存雪崩是因为大面积的缓存同一时刻同时失效,导致用户请求直接访问DB。导致DB瘫痪,而缓存击穿是指Redis中的某一个key非常热点,在不停的扛着大量的并发,大量并发集中对这一个点进行访问,当这个key在失效的一瞬间,持续着的大量并发会穿破缓存,直接访问DB,就像在一个完好无损的桶上凿开了一个洞。
缓存穿透和缓存击穿的解决方式
缓存穿透
缓存穿透的解决方法通常是在接口层增加校验,比如用户鉴权校验、参数校验,不合法的参数直接代码return。比图:id做基础校验,id<=0的直接拦截。
或者当从缓存中取不到相应数据并且在数据库中也查询不到时,这时可以将对应的key的value写为null、未知错误、稍后重试等。这样可以防止用户反复用同一个id暴力攻击,但是对于普通用户来说,也不会有这种操作,此时可以让运维人员对单个IP每秒访问次数超过阈值的IP拉黑。
或者使用Redis自带的布隆过滤器,这个也可以很好的防止缓存穿透的发生,它的原理也很简单,就是利用高效的数据结构和算法判断出用户发起请求的key在数据库中是佛存在,不存在的话直接return,若存在的话从DB中查到之后刷新Redis中的KV。
缓存击穿
缓存击穿的话,设置热点数据永远不会过期,或者加上互斥锁就能解决。
总结
Redis的缓存雪崩、缓存穿透、缓存击穿三者其实差不多,但是又有一些区别。
防止这些问题的方式
- 事前:Redis高可用、主从+哨兵或者Redis Cluster,避免Redis集群的全盘崩溃。
- 事中:本地ehcache缓存+Hystrix限流+降级,避免Mysql崩溃。
- 事后:Redis持久化RDB+AOF,一旦重启,自动从磁盘加载数据,快速恢复缓存数据。