本文为阅读笔记,主要内容源自极客时间蒋德均老师的《Redis核心技术与实战》。
我们知道,我们使用Redis的主要用途就是做缓存,一般来说,根据Redis缓存是否接受读写请求,我们可以把它分为只读缓存和读写缓存。
所谓的只读缓存,指的是查询数据的时候先从Redis查询,如果未命中那么才会从数据库查询并SET到Redis缓存中,而在对数据进行删改的时候,除了本身操作数据库之外,还会把相应的缓存数据删除,保证Redis下次查询的时候不会命中。而读写缓存指的是对数据的所有读请求和写请求都会发送到缓存中,在缓存中对数据进行增删改操作。另外,对于读写缓存来说,还存在同步直写和异步写回两种策略,具体的区别可以根据下图理解。
但是在实际的应用过程中,我们经常会遇到一些异常问题,总结下来有以下几点:
- 缓存和数据库的一致性问题
- 缓存雪崩
- 缓存击穿
- 缓存穿透
本文将对这几个问题进行展开,具体讲讲 什么时候下会引起这些问题 以及解决的方案是什么。
数据一致性问题
所谓的数据一致性问题,就是因为某些原因导致的Redis缓存中数据与数据库数据不一致的问题。
对于读写缓存来说,如果缓存的数据本身对数据一致性要求不高,是可以采用性能更好的异步写回策略的;对于其他数据,想要保证数据一致性,那么就要采用同步直写策略。
但是无论是只读缓存还是读写缓存的同步直写策略,都必须保证对Redis和数据库的操作必须是原子性的,两者有任何一边失败,都会带来数据一致性问题。
对于上面这两种情况,我们可以根据实际情况(是否有并发请求)来决定采用什么方案。
在大多数业务场景下,我们会把 Redis 作为只读缓存使用。针对只读缓存来说,我们既可以先删除缓存值再更新数据库,也可以先更新数据库再删除缓存。我的建议是,优先使用先更新数据库再删除缓存的方法,原因主要有两个:
- 先删除缓存值再更新数据库,有可能导致请求因缓存缺失而访问数据库,给数据库带来压力;
- 如果业务应用中读取数据库和写缓存的时间不好估算,那么,延迟双删中的等待时间就不好设置。
不过,当使用先更新数据库再删除缓存时,也有个地方需要注意,如果业务层要求必须读取一致的数据,那么,我们就需要在更新数据库时,先在 Redis 缓存客户端暂存并发读请求,等数据库更新完、缓存值删除后,再读取数据,从而保证数据一致性。
缓存雪崩
定义
所谓缓存雪崩,指的是大量的应用请求无法在Redis缓存中进行处理,从而导致大量请求发到数据库层,导致数据库层的压力激增。
原因及处理方案
一般由两个原因导致,针对两种原因的处理方案也不一样。
第一种,缓存中有大量数据同时过期,导致应用直接去请求数据库。
针对这种情况,我们可以通过避免给大量数据设置同样的过期时间,如果确实有需求要求缓存同时失效,也可以给失效时间加上一个随机数(比如1~3分钟)。
此外,我们还可以通过服务降级来应对缓存雪崩。
所谓服务降级,是指发生缓存雪崩时,针对不同的数据采取不同的处理方式。例如,针对非核心数据,可以暂时停止查询缓存,直接返回预定义信息或者错误信息;针对核心数据,仍然允许查缓存并且读取数据库,这样数据库的压力就不会那么大了。
第二种,Redis缓存实例发生故障宕机了。
如果是重度依赖Redis缓存的系统,由于Redis的吞吐量和数据库相比远远超出,所以这种情况下数据库很可能因为压力过大而崩溃。
如果出现这种情况,我们可以在业务系统中实现服务熔断或请求限流机制。通过限制Redis缓存请求或者限制请求的频率,避免过多的请求到达数据库来临时应对;当然了,为了防止这种情况的发生,最重要的还是要做好事前预防。比如通过哨兵、集群来尽量保证Redis的高可用。
缓存击穿
缓存击穿指的是,针对某些访问特别频繁的热点数据的请求,无法在缓存中处理,紧接着全部到达了后端数据库,导致数据库压力激增而影像处理其他请求。对于这种情况,建议不要给热点数据设置过期时间了,这样一来,对热点数据的访问请求,都可以在缓存中进行处理,而 Redis 数万级别的高吞吐量可以很好地应对大量的并发请求访问。
缓存穿透
定义
缓存穿透是指要访问的数据既不在Redis中,也不在数据库中,导致每次这样的请求都会先访问缓存,再去访问数据库,如果短时间内有大量这样的请求,那么就会同时给缓存和数据库带来压力。
原因及处理方案
一般来说,有两种情况:第一种是业务层误操作,误删除了数据,导致缓存和数据库中都没有数据,第二种是有人恶心攻击,即专门访问数据库中没有的数据。
这里提供三种处理方案。
**第一种方案是,缓存空值或者缺省值。**这个很好理解,当出现这种数据时,在Redis中缓存空值或者缺省值,这样就避免了大量请求到数据库了。
第二种方案是,使用布隆过滤器快速判断数据是否存在,避免从数据库中查询。
第三种方案是,在请求入口的前端进行请求检测。 缓存穿透的一个原因是有大量的恶意数据请求访问不存在的数据,这时候可以在前端对请求进行合法性检测,过滤恶意的请求(例如参数不合理、非法值等等),这样就不会出现缓存穿透问题了。
总结
针对缓存雪崩、缓存击穿、缓存穿透这三种缓存异常,对应的原因处理方案,整理在下图了,方便快速复习。
其中,服务熔断、服务降级、请求限流这些方法都是属于“有损”方案,在保证数据库和整体系统稳定的同时,会对业务应用带来负面影响。所以,在实际开发中,尽量使用”预防式方案“:
- 针对缓存雪崩,合理设置数据过期时间以及搭建高可用的集群
- 针对缓存击穿,不要给热点数据设置过期时间
- 针对缓存穿透,提前在入口前端实现恶意请求检测,或者规范删除操作防止误删除