目录
前言
一、缓存雪崩
1.大量数据同时过期
1.均匀设置过期时间
2.互斥锁
3.后台更新缓存
2.Redis故障宕机
1.服务熔断或请求限流机制
2.构建Redis缓存高可靠集群
二、缓存击穿
1.设置互斥锁;
2.不给热点数据设置过期时间,由后台更新缓存。
三、缓存穿透
1.非法请求的限制
2.缓存空值或者默认值
3.使用布隆过滤器快速判断数据是否存在,避免通过查询数据库来判断数据是否存在
布隆过滤器
总结
前言
用户的数据一般存在数据库,也就是磁盘里。
如果用户请求全访问数据库的话,数据库很容易崩溃,一般在他们中间回家一个Redis作为缓存层。(Redis是内存数据库,相当于数据缓存在内存里面,内存的读写速度比硬盘快好几个数量级,系统性能大大提升)
当缓存层出现异常的时候就可能遇到三个问题:
缓存雪崩、缓存击穿、缓存穿透。
一、缓存雪崩
有两种情况会出现缓存雪崩:
1.大量缓存数据同时过期
2.Redis故障
为了保证缓存中的数据和数据库中的数据一致性,会给Redis里的数据设置过期时间,当缓存数据过期后,用户访问的数据如果不在缓存里,业务系统需要重新生成缓存,因此就会访问数据库,并将数据更新到Redis里面,这样后续请求都可以直接命中缓存。
但是,当大量缓存数据在用一时间过期或者Redis故障宕机时,如果此时有大量的用户请求,都无法在Redis中处理,于是又全部都直接访问数据库,从而导致数据库的压力骤增,严重的会造成数据库宕机,可能造成整个系统崩溃,这就是缓存雪崩。
1.大量数据同时过期
大量数据同事过期导致的缓存雪崩可以:均匀设置过期时间,互斥锁,后台更新缓存。
1.均匀设置过期时间
在缓存数据设置过期时间时,给这些数据的过期时间随机加上一个随机数,保证他们不会在同一时间过期。
2.互斥锁
在用户访问时加互斥锁,当用户访问的数据不在Redis里,加上互斥锁,保证同一时间内只有一个请求来构建缓存 (从数据库读取数据,再将数据更新到Redis里),当缓存构建完成后,再释放锁。如果有请求未能获取互斥锁,要么等待所释放后重新读取缓存,要么就返回空值或者默认值。
实现互斥锁的时候要设置超时时间,不然第一个请求拿到了锁,然后这个请求发生了某种意外而一直阻塞,一直不释放锁,这个时候其他请求也一直拿不到锁,整个系统就会出现无响应的现象。
3.后台更新缓存
缓存不设置有限期,而是让缓存永久有效,将更新缓存的工作交由后台线程定时更新。
这里也有一个问题。缓存不设置有效期并不是意味着数据能一直在内存里,因为当系统内存紧张的时候,有些缓存数据会被淘汰,而在缓存被淘汰到下一次后台定时更新缓存的这段时间内,业务线程读取缓存失败就返回空值,业务的视角就以为是数据丢失。
针对于业务视角的数据丢失问题,有两种解决方式。
1.后台线程不仅负责定时更新缓存,而且也负责频繁地检测缓存是否有效,如果检测到缓存失效了,马上从数据库中读取数据,并更新到缓存。(这个解决方式的检测时间间隔不能太长,太长也就导致用户获取的数据是一个空值而不是真正的数据,所以检测的间隔时间最好是毫秒级,但是总归是有个间隔时间,用户体验一般)
2.第二种方式就会好很多,因为它在业务线程发现缓存数据失效后,才通过消息队列发送一条消息通知后台线程更新缓存,后台线程收到消息后也要先去检测缓存是否存在,不存在才读取数据库数据,并将数据加载到缓存。(这方式的更新会更及时,用户体验提高)
缓存预热:在业务刚上线的时候,最好是提前把数据缓存起来,而不是等待用户访问才来触发缓存构建,就是缓存预热,后台更新缓存的机制刚好也适合干这个事情。
2.Redis故障宕机
常见的应对方式:服务熔断或者请求限流机制;构建Redis缓存高可靠集群。
1.服务熔断或请求限流机制
服务熔断机制:暂停业务应用对缓存服务的访问,直接返回错误。等到Redis恢复正常后,再允许业务应用访问缓存服务。
请求限流机制:服务熔断会导致全部业务都无法正常工作,请求限流机制的话,就只将少部分请求发送到数据库进行处理,再多的请求就在入口直接拒绝服务,等到Redis恢复正常并把缓存预热完后,在解除请求限流的机制。
2.构建Redis缓存高可靠集群
之前说的服务熔断或者请求限流机制是缓存雪崩发生后的应对方案,为了避免由于Redis故障宕机导致的缓存雪崩问题,最后通过主从节点的方式构建Redis缓存高可靠集群,如果Redis缓存的主节点故障宕机,从节点可以切换成为主节点,继续提供缓存服务。
二、缓存击穿
缓存击穿可以看成缓存雪崩的一个子集。
如果缓存中的某个热点数据过期了,此时大量的请求访问了该热点数据,就无法从缓存中读取,直接访问数据库,数据库很容易就被高并发的请求冲垮了。这就是缓存击穿的问题。
解决方式可以用到前面说的:
1.设置互斥锁;
2.不给热点数据设置过期时间,由后台更新缓存。
三、缓存穿透
当用户访问的数据,既不在缓存中,也不在数据库中,没办法构建缓存数据。。。
当有大量这样的请求来到时,数据库的压力骤增。
一般由两种情况导致:业务误操作,导致缓存中的数据和数据库中的数据都被错误删除了,所以缓存和数据库中都没有数据;黑客恶意攻击,故意大量访问某些不存在数据的业务。
应对方案:非法请求的限制;缓存空值或者默认值;使用布隆过滤器快速判断数据是否存在,避免通过查询数据库来判断数据是否存在。
1.非法请求的限制
在API入口处判断请求参数是否合理,比如请求参数是否含有非法制、请求字段是否存在,如果判断出是恶意请求就直接返回错误,避免进一步访问缓存和数据库。
2.缓存空值或者默认值
当线上业务发现缓存穿透的现象时,可以针对查询的数据,在缓存中设置一个空值或者默认值,这样后续请求就可以从缓存中读取到空值或者默认值,返回给应用,而不会继续查询数据库。
3.使用布隆过滤器快速判断数据是否存在,避免通过查询数据库来判断数据是否存在
在写入数据库数据时,使用布隆过滤器做一个标记,然后在用户请求到来时,业务线程确认缓存失效后,可以通过查询布隆过滤器快速判断数据是否存在,如果不存在,就不用通过查询数据库来判断数据是否存在。
即使发生了缓存穿透,大量请求只会查询Redis和布隆过滤器,而不会查询数据库,保证了数据库能正常运行,Redis自身也是支持布隆过滤器的。
布隆过滤器
布隆过滤器有初始值都是0的位图数组和N个哈希函数两部分组成。当写入数据库数据时,在布隆过滤器里做个标记,这样下次查询数据是否在数据库时,就只要查询布隆过滤器,如果查询到数据没有内标记,就说明该数据就不在数据库中。
第一步,使用n个哈希函数分别对数据做哈希计算,得到n个哈希值;
第二步,将第一步得到的n个哈希值对位图数组的长度取模,得到每个哈希值在位图数组的对应位置。
第三步,将每个哈希值在位图数组的对应位置的之后设置为1。
比如,这个n可以设置为3,也就是如果一个数据存在,那么它在位图数组中有三个位置为1,查询时,只要判断这个数据对应的三个位置是不是都是1,只要有一个为0,就认为该数据不存在于数据库中。
当然,布隆过滤器会有哈希冲突的可能性,比如两个不同的数据,在相同的位置都是1,但是只存在其中一个数据时,就会存在误判的情况。
因此,查询布隆过滤器判断数据是否存在时,并不一定能证明数据库中存在这个数据,但是查询布隆过滤器不存在该数据时,那么,该数据就一定不在数据库中。
总结
过段时间我来再背一遍。
什么是缓存雪崩、击穿、穿透? | 小林coding (xiaolincoding.com)