缓存雪崩
缓存雪崩是指大量热点数据同时过期,导致请求直接落到数据库,对数据库造成巨大压力甚至宕机。
解决方案:
1.简单方案
不让缓存同时过期。可以在过期时间上加一定范围的随机数,也可以将一些几乎不改变的数据设置为永远不过期。
2.复杂方案
分布式缓存+多个数据库,使请求尽可能分布在不通的redis 和mysql数据库中,在过期时间有限制的场景,也可以使用该方案,使多个库共同分担压力。
缓存雪崩的事前事中事后的解决方案如下。
- 事前:redis 高可用,主从+哨兵,redis cluster,避免全盘崩溃。
- 事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。
- 事后:redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
缓存穿透
缓存穿透是指大量请求不命中缓存,直接穿透到数据库。可能是由黑客频繁请求某些不存在的id ,比如持续请求id>一千万的一系列数据,因为这些数据缓存里不存在,所以每次都会请求数据库,即产生了缓存穿透。
解决办法:
1.初级方案:
将不存在的数据也缓存起来。比如某个数据不存在,在redis中就缓存一个null,下次再请求这个数据就会命中缓存了。
2.优化方案:
方案一可以解决单个key的穿透问题,但如果黑客拿一系列不存在的key,就会在redis产生大量值为null的key ,占满内存,很快也会拖垮我redis。那么我们可以使用一个set 将存在的key都存在这个set里,取的时候先从set里查看下,如果存在再读缓存,如果不存在直接返回null。注意需要该set的预热。
3.终极方案:
方案2中用于过滤的set在数据量少的时候可以使用,如果数据量巨大,那么该set将会占用大量的内存。所以需要祭出缓存穿透的终极方案,布隆过滤器。在redis 4.0以后都支持布隆过滤器,其结构就是一个位数字,由于每个元素只占1位,所以100000个元素,也只有100000/8=12500 即越12KB内存,大大减轻了redis内存负担。
布隆过滤器的工作原理,是将key进行多次hash运算,比如存在三个哈希函数,就进行三次运算,分别得到的值为s1,s2,s3。再将位数组中对应的位置为1。
查找的时候再对key进行三次运算,对应位都位1则说明该值可能存在。
如果有一个位置不为1,则返回不存在。
注意事项1:
使用布隆过滤器要考虑到哈希碰撞的情况,也就是随着数据的增多,有可能两个不同的key三次哈希运算得到的值是一样的,所以会产生误判该值存在的情况。这个错误率,也是布隆过滤器可调节的指标之一。
所以在使用布隆过滤器时,最好通过该值不存在进行判断更为准确。
redis 布隆过滤器使用方法:
//创建一个布隆过滤器,并设置错误率为万分之一,容量100w
bf.reserve one-more-filter 0.0001 1000000
//添加key
bf.add one-more-filter fans1
//判断key是否存在
bf.exists one-more-filter fans1
注意事项2:
错误率的设置会影响内存占用,越低说明判断越准确,所需要的内存占用也就越高。
缓存击穿
缓存击穿是指在热点key失效时,同时有大量请求过来,同时透传到数据库,造成数据库压力或崩溃。
解决办法:
解决办法就是加锁,在请求数据库操作,只允许一个请求进行,其他请求等待该请求返回,再从redis中读取数据。在spring cache中这个操作非常简单,只需要在注解中加上sync=true,如@Cacheable(value = “test”, key = “#a0”, sync = true)。非springcache可以考虑使用个分布式锁,如zookeeper或redis锁用来实现此功能。