为了能够对请求进行快速响应,出现了缓存。它能利用内存的高速读写性能来应付海量的查询请求。然而内存资源非常宝贵,将全部数据都放在内存显然是不切实际的。
目前都是采用内存和磁盘结合的方式,内存只存储热点数据,而 IO 设备存储全部的数据。本文将对缓存以及热点数据中存在的问题进行讨论。
缓存穿透
互联网应用中缓存的使用模型:
- 业务系统发起查询请求,判断缓存中是否有该数据。
- 如果缓存中存在,直接返回数据。
- 如果缓存中不存在,则再查询数据库,然后返回数据。
什么是缓存穿透
缓存中不存在该业务数据,会直接前往数据库中查询,但是数据根本不存在,数据返回也为空,这里称为缓存穿透。
缓存穿透的危害
如果还想请求查询压根就不存在的数据,那么这些海量请求都会落到数据库中,数据库压力过大会导致崩溃。
解决方案
-
缓存空数据
之所以发生缓存穿透,是因为缓存中没有存储这些空数据的 key,导致这些请求全部打到了数据库上。
我们可以稍微修改业务系统的代码,将查询结果为空的 key 也缓存到缓存中个,这样后续该 key 的请求就无需查询数据库了。
-
屏障 BloomFilter
它需要在缓存之前加一道屏障,里面存储目前数据库中的所有 key。Redis 中有高级用法叫布隆过滤器。
当业务系统有查询请求的时候,首先去 BloomFilter 中查询该 key 是否存在。若不存在,则说明数据库也不存在该数据,因此缓存都不用查了。
两种方法的比较
对于一些恶意攻击,查询的 key 往往不同,而且数据极多。此时第一种方案就显得捉襟见肘了。所以对于空数据的 key 各不相同,key 重复概率低的场景而言,应该选择第二种方案。对于空数据的 key 数量有限,key 重复请求概率较高的场景而言,应该选择第一种方案。
缓存雪崩
什么是缓存雪崩?
通过上文可知,缓存其实扮演了一个保护数据库的角色。它帮助数据库抵挡了大量的查询请求,从而避免脆弱的数据库受到伤害。
如果缓存因为某种原因发生了宕机,那么原本被缓存抵挡的海量请求就会直接涌向数据库,数据库不堪重负就会崩溃,这就是缓存雪崩。
解决方案
-
使用缓存集群,保证缓存高可用。也就是在发生雪崩之前,做好防护。
-
Hystrix
Hystrix 是一款开源的放雪崩工具,它通过熔断,降级,限流三个手段来降低雪崩发生后的损失。Hystrix 一旦发现当前服务的请求失败率达到预设的值后,Hystrix 会拒绝随后该服务的所有请求,直接返回一个预设的结果,这就是熔断。过一段时间,Hystrix 会放行一部分请求,在此统计请求失败率,如果符合预设值,会打开限流开关,如果失败率仍然很高,就会拒绝该服务所有请求,这就是限流。Hystrix 向被拒绝的请求返回预设结果被称为降级。
缓存击穿
什么是缓存击穿?
我们一般都会给缓存设定一个失效时间,过了时间后,该数据会被删除,从而在一定程度上保证数据的实时性。
对于一些请求量极高的数据而言,一旦过了有效时间,此刻会有大量的请求落在数据库上,从而可能会导致数据库崩溃。
!
从缓存失效到查询数据库后更新缓存的时间,肯能会有许多请求同时到达,此时,它们都会查询数据库,而且会重复更新缓存。
解决方案
-
互斥锁
可以使用缓存自带的锁机制,当一个数据库查询请求发起时,就将该缓存中该数据上锁。此时到达缓存的其他查询无法查询该字段,从而被阻塞。直到第一个请求完成数据库查询并更新缓存后,释放锁。
热点数据集中失效问题
什么是热点数据集中失效的问题?
我们在设置缓存的时候,一般会给缓存设置一个失效时间,过了这个时间,缓存就失效了。对于一些热点的数据来说,当缓存失效以后会存在大量的请求过来,然后打到数据库去,从而可能导致数据库崩溃的情况。
解决方案
设置不同的失效时间
- 当我们向缓存中存储这些数据的时候,可以将他们的缓存失效时间错开。这样能够避免同时失效。比如在基础时间上加一个随机数,从而将缓存的失效时间错开。
- 互斥锁。