一、雪崩
缓存雪崩在面试redis中经常会被问到,通常的回答会是缓存雪崩是因为大面积的缓存失效,打崩了DB。但这么说有点笼统,可以再详细的举例说说
1. 原因:
那么缓存雪崩的原因都可能有哪些?
- redis实例宕机
redis实例宕机这点很好理解,就是redis挂了,本来会先访问redis获取数据,redis挂掉以后,无法从redis获取到数据了,流量都打到数据库上了,数据库承受不住如此大的流量,于是数据库也挂了。
- 大量数据同时失效
大量数据同时失效,这点可能是属于人祸,一般情况想让大量数据同时失效也很难的,除非比如被人调用了flushall
命令,这种情况我还真的见过,在原来公司的测试环境,就真的会有人连接公用的redis,然后调用flushall
命令清除测试数据,结果把别人的测试数据也一块清除掉了。另外一种情况,是公司封装的redis
工具包,自动设置key的超时时间为当天的12:00,到12点所有的key一块过期,够刺激吧,虽然这可能是业务需要,但是这种操作真的台危险了。
2. 解决
解决方案分为一下几个阶段:
事前:
redis高可用,redis实例部署要有主从,非集群的实例,可以部署sentinel自动故障时切换主从,集群类型的实例天然是高可用的,在此基础上所有的集群的主实例都添加从实例就可以了。另外还有一些其他的部署规范,如:主从实例不能部署到同一台机器上等等。
均摊分配redis key的失效时间,只要不要集中在一点就好,以几分钟的随机数让缓存错峰失效
事中:应用程序集成限流和降级的功能,在redis崩溃或者数据大量失效的情况下降低访问或返回默认结果,避免DB被流量被打死。
事后:使用redis持久化数据快速恢复缓存数据。RDB和AOF两种方式各有优势和缺点,AOF丢失的数据会是少一些。
二、穿透
1. 原因:
缓存穿透原因相应的比较简单,就是获取不存在的数据。正常情况下,接口从缓存中获取数据,如果获取不到,会从数据库中查询数据并设置到缓存中。但是这个数据如果在数据库中没有,那每次的获取就会一直从数据库中查询,缓存没有起到作用。流量大的话全部打到数据库上,就把数据库给打死了。
2. 解决:
这种情况一般的解决方案是在从数据库中获取不到值时,就在缓存中给对应的key设置一个自定义的值,比如我们公司是设置一个自定义的EmptyObject对象,下次获取值时,如果发现是EmptyObject对象的话,就返回null
值,如果不是就查库,但是要注意一点,设置EmptyObject对象的Key应该设置一个较短有效期,这样,既可以防止频繁查询数据库造成穿透,又可以防止后续数据库中可以获取到值但从缓存中获取仍是null
值的情况。
另外应该从接口方面限制参数规则验证,如果参数不符合规则直接抛异常,常见的如对外提供的接口,参数应该是大于0的,如果传入小于等于0的参数,则直接返回错误。
三、击穿
缓存击穿是指一个Key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个Key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库。
缓存击穿的解决方案一个是使用数据库的二级缓存,使用ehcache实现二级缓存高并发的流量不会都打到数据库上,另外就是使用限流工具,限制访问数据库的接口的流量。