理解与解决缓存穿透、缓存击穿、缓存雪崩、热点数据失效问题

1、前言

在咱们的日常的项目中多多少少都会使用到缓存,由于一些数据咱们没有必要每次查询的时候都去查询到数据库。数据库

特别是高 QPS 的系统,每次都去查询数据库,对于你的数据库来讲将是灾难。缓存

今天咱们不牵涉多级缓存的知识,就把系统使用到的缓存方案,不论是一级仍是多级的都统称为缓存,主要是为了讲述使用缓存的时候可能会遇到的一些问题以及一些解决办法。并发

咱们使用缓存时,咱们的业务系统大概的调用流程以下图:高并发

使用缓存的业务系统调用流程

当咱们查询一条数据时,先去查询缓存,若是缓存有就直接返回,若是没有就去查询数据库,而后返回。这种状况下就可能会出现一些现象。post

2、缓存穿透

一、什么是缓存穿透

正常状况下,咱们去查询数据都是存在。那么请求去查询一条压根儿数据库中根本就不存在的数据,也就是缓存和数据库都查询不到这条数据,可是请求每次都会打到数据库上面去。这种查询不存在数据的现象咱们称为缓存穿透大数据

二、穿透带来的问题

试想一下,若是有黑客会对你的系统进行攻击,拿一个不存在的id去查询数据,会产生大量的请求到数据库去查询。可能会致使你的数据库因为压力过大而宕掉。url

三、解决办法

1)、 缓存空值

之因此会发生穿透,就是由于缓存中没有存储这些空数据的key。从而致使每次查询都到数据库去了。那么咱们就能够为这些key对应的值设置为null丢到缓存里面去。后面再出现查询这个key的请求的时候,直接返回null。这样就不用在到数据库中去走一圈了,可是别忘了设置过时时间。线程

2)、BloomFilter

BloomFilter 相似于一个hbase set 用来判断某个元素(key)是否存在于某个集合中。blog

这种方式在大数据场景应用比较多,好比 Hbase 中使用它去判断数据是否在磁盘上。还有在爬虫场景判断url 是否已经被爬取过。内存

这种方案能够加在第一种方案中,在缓存以前在加一层 BloomFilter ,在查询的时候先去 BloomFilter 去查询 key 是否存在,若是不存在就直接返回,存在再走查缓存 -> 查 DB。

流程图以下:
BloomFilter的流程图

四、如何选择

针对于一些恶意攻击,攻击带过来的大量key是不存在的,那么咱们采用第一种方案就会缓存大量不存在key的数据。此时咱们采用第一种方案就不合适了,咱们彻底能够先对使用第二种方案进行过滤掉这些key。针对这种key异常多、请求重复率比较低的数据,咱们就没有必要进行缓存,使用第二种方案直接过滤掉。而对于空数据的key有限的,重复率比较高的,咱们则能够采用第一种方式进行缓存。

3、缓存击穿

一、什么是击穿

缓存击穿是咱们可能遇到的第二个使用缓存方案可能遇到的问题。在日常高并发的系统中,大量的请求同时查询一个key时,此时这个key正好失效了,就会致使大量的请求都打到数据库上面去。这种现象咱们称为缓存击穿

二、会带来什么问题

会形成某一时刻数据库请求量过大,压力剧增。

三、如何解决

上面的现象是多个线程同时去查询数据库的这条数据,那么咱们能够在第一个查询数据的请求上使用一个互斥锁来锁住它。其余的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,而后作缓存。后面的线程进来发现已经有缓存了,就直接走缓存。

4、缓存雪崩

一、什么是缓存雪崩

缓存雪崩的状况是说,当某一时刻发生大规模的缓存失效的状况,好比你的缓存服务宕机了,会有大量的请求进来直接打到DB上面。结果就是DB撑不住,挂掉。

二、解决办法

1)、事前:

使用集群缓存,保证缓存服务的高可用这种方案就是在发生雪崩前对缓存集群实现高可用,若是是使用 Redis,可使用 主从+哨兵 ,Redis Cluster 来避免 Redis 全盘崩溃的状况。

二、事中:

ehcache本地缓存 + Hystrix限流&降级,避免MySQL被打死使用 ehcache 本地缓存的目的也是考虑在 Redis Cluster 彻底不可用的时候,ehcache 本地缓存还可以支撑一阵。使用 Hystrix进行限流 & 降级 ,好比一秒来了5000个请求,咱们能够设置假设只能有一秒2000个请求能经过这个组件,那么其余剩余的3000请求就会走限流逻辑。而后去调用咱们本身开发的降级组件(降级),好比设置的一些默认值呀之类的。以此来保护最后的MySQL不会被大量的请求给打死。

3)、过后:

开启Redis持久化机制,尽快恢复缓存集群一旦重启,就能从磁盘上自动加载数据恢复内存中的数据。防止雪崩方案以下图所示:

防止雪崩方案

5、解决热点数据集中失效问题

咱们在设置缓存的时候,通常会给缓存设置一个失效时间,过了这个时间,缓存就失效了。对于一些热点的数据来讲,当缓存失效之后会存在大量的请求过来,而后打到数据库去,从而可能致使数据库崩溃的状况。

解决办法以下:

一、设置不一样的失效时间

为了不这些热点的数据集中失效,那么咱们在设置缓存过时时间的时候,咱们让他们失效的时间错开。好比在一个基础的时间上加上或者减去一个范围内的随机值。

二、互斥锁

结合上面的击穿的状况,在第一个请求去查询数据库的时候对他加一个互斥锁,其他的查询请求都会被阻塞住,直到锁被释放,从而保护数据库。可是也是因为它会阻塞其余的线程,此时系统吞吐量会降低。须要结合实际的业务去考虑是否要这么作。

阅读原文

相关文章
相关标签/搜索