前言
我们平常的项目中多多少少都会使用到缓存,因为一些数据我们没有必要每次查询都查询数据库,对于高并发的项目,每次都查询数据库,对数据库是灾难性的!!!
当我们查询一条数据的时候,先去查询缓存,如果有缓存就直接返回,如果没有才会去查数据库,然后返回。
缓存穿透
什么是缓存穿透
正常情况下,我们去查询数据都是存在。
那么请求去查询一条压根儿数据库中根本就不存在的数据,也就是缓存和数据库都查询不到这条数据,但是请求每次都会打到数据库上面去。
这种查询不存在数据的现象我们称为缓存穿透。
会带来的问题
如果有黑客会对你的系统进行攻击,拿一个不存在的id 去查询数据,会产生大量的请求到数据库去查询。可能会导致你的数据库由于压力过大而宕掉。
怎么解决
- 缓存空值
之所以会发生穿透,就是因为缓存中没有存储这些空数据的key。从而导致每次查询都 到数据库去了。那么我们就可以为这些key对应的值设置为null 丢到缓存里面去。后面再出现查询这个key 的请求的时候,直接返回null 。这样,就不用在到数据库中去走一圈了 - BloomFilter(布隆过滤器)
布隆过滤器可以用于检索一个元素是否在一个集合中。
优点是空间效率和查询时间都远远超过一般的算法,
缺点是有一定的误识别率和删除困难。
原理:
布隆过滤器的原理是,当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。这就是布隆过滤器的基本思想。
图片源地址
简而言之,言而简之就是我们先把我们数据库的数据都加载到我们的过滤器中,比如数据库的id现在有:1、2、3
那就用id:1 为例子他在上图中经过三次hash之后,把三次原本值0的地方改为1
下次数据进来查询的时候如果id的值是1,那么我就把1拿去三次hash 发现三次hash的值,跟上面的三个位置完全一样,那就能证明过滤器中有1的
反之如果不一样就说明不存在了
怎么选择方案
- 针对于一些恶意攻击,攻击带过来的大量key 是不存在的 ——> 方案二
- 对于空数据的key有限的,重复率比较高的 ——> 方案一
缓存击穿
什么是缓存击穿
在高并发的系统中,大量的请求同时查询一个key时,此时key正好失效,就会导致大量的请求直接打到DB上,这种现象称作缓存击穿
带来的问题
造成某一时间点数据库请求量过大,压力剧增,可能导致数据库服务宕机
如何解决
多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个 互斥锁来锁住它。其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。后面的线程进来发现已经有缓存了,就直接走缓存。(俗称的双重检测)
缓存雪崩
什么是缓存雪崩
某一时刻缓存大规模的失效(缓存服务宕机、大量缓存失效…)会有大量的请求进来直接打到DB上。
带来的问题
大量请求打到DB上,导致DB宕机
如何解决
事前:
- 热点数据过期时间设置为不过期
- 使用集群缓存,保证缓存服务的高可用
这种方案就是在发生雪崩前对缓存集群实现高可用,如果是使用 Redis,可以使用 主从+哨兵 ,Redis Cluster 来避免 Redis 全盘崩溃的情况。
事中:
- 本地缓存、限流降级,避免DB被打挂
- 使用 ehcache 本地缓存的目的也是考虑在 Redis Cluster 完全不可用的时候,ehcache 本地缓存还能够支撑一阵。
- 使用 Hystrix进行限流 & 降级 ,比如一秒来了5000个请求,我们可以设置假设只能有一 秒 2000个请求能通过这个组件,那么其他剩余的 3000 请求就会走限流逻辑。然后去调用我们自己开发的降级组件(降级),比如设置的一些默认值呀之类的。以此来保护最后的 MySQL 不会被大量的请求给打死。
事后:
- 开启Redis持久化机制,尽快恢复缓存集群
一旦重启,就能从磁盘上自动加载数据恢复内存中的数据。