缓存的3个问题和解决方案

2020-01-08  本文已影响0人  紫色红色黑色

描述

本文翻译3 major problems and solutions in the cache world。文中解释了缓存穿透(Cache penetration)、缓存雪崩(Cache avalanche)、热点数据失效,及其解决方法。

全文

目前的 IO 设备远远不能满足互联网应用程序的大量读写请求。 然后使用内存的高速读写性能来处理大量的查询请求的缓存诞生。 然而,内存资源宝贵,在内存中存储全部数据显然是不切实际的。 因此,使用内存和 IO 组合,内存只存储热点数据,而 IO 设备存储全部数据。 缓存设计有很多技巧,不正确的设计可能会导致严重的后果。 本文将介绍缓存使用中经常遇到的三个主要问题,并给出相应的解决方案。

1 缓存穿透

在大多数互联网应用程序中:

理解了上面的过程之后,让我们讨论一下缓存穿透。

1.1 什么是缓存穿透

业务系统要查询的数据不存在!当业务系统根据上述流程启动查询时,查询首先转到缓存,因为缓存不存在,会转到数据库进行查询。 因为数据根本不存在,所以数据库也返回 null。 这是缓存穿透。

总之,业务系统访问根本不存在的数据称为缓存穿透。

1.2 缓存穿透的危险

如果大量查询请求不存在的数据,那么这些请求将进入数据库,数据库压力将急剧增加,这可能导致系统崩溃。 (你必须知道,当前商业系统中最脆弱的是 IO,它会在一点点压力下崩溃,所以我们必须想办法保护它)。

1.3 为什么会发生缓存穿透

导致缓存穿透的原因有很多,一般如下:

1.4 缓存穿透解决方案

这里有两种防止缓存穿透的方法。

1.4.1 缓存空数据

缓存穿透的原因是缓存中没有 key 来存储这些空数据,导致所有这些请求都命中数据库。

然后,我们可以稍微修改业务系统的代码,并将数据库空查询结果的 key 存储在缓存中。 当再次发生该 key 的查询请求时,缓存直接返回 null,而不查询数据库。

1.4.2 BloomFilter

避免缓存穿透的第二种方法是使用 BloomFilter。

它需要在缓存之前添加一个屏障,存储当前数据库中存在的所有 key。

当业务系统有一个查询请求时,首先到 BloomFilter 检查 key 是否存在。 如果它不存在,则意味着数据不存在于数据库中,因此不应该查询缓存而直接返回 null。 如果存在,则继续执行后续流程,首先查询缓存,如果没有缓存,则查询数据库。

1.4.3 两个方法比较

这两种解决方案都可以解决缓存穿透问题,但是使用场景不同。

对于一些恶意攻击,查询的 key 通常是不同的,对于数据窃取者更甚。 在这一点上,第一个方案 key 太多了。 因为它需要存储所有空数据的 key,而这些恶意攻击的 key 通常是不同的,同一个 key 通常只请求一次。 因此,即使缓存了这些空数据的 key,由于不再使用第二次,也无法实现保护数据库的作用。 因此,对于空数据的 key 不同且重复请求的概率较低的情况,应该选择第二种方案。 对于空数据的 key 数量有限且重复请求的概率很高的情况,应该选择第一个方案。

2 缓存雪崩

2.1 什么是缓存雪崩

上述而知,缓存实际上起到了保护数据库的作用。 它帮助数据库承受大量的查询请求,从而保护脆弱的数据库。

如果由于某种原因缓存中断,原本被缓存处理的大量查询请求将像疯狗一样涌向数据库。 在这一点上,如果数据库不能承受如此巨大的压力,它就会崩溃。

这就是缓存雪崩。

2.2 如何避免缓存雪崩

2.2.1 使用缓存集群确保缓存高可用

也就是说,在雪崩发生之前,要采取预防措施来防止雪崩的发生。 Ps:分布式高可用性的问题不是今天讨论的重点。 请注意,正常情况下请遵循高可用性相关的规范。

2.2.2 使用 Hystrix

Hystrix 是一个开源的”反雪崩工具” ,通过熔断、降级和限流来减少雪崩后的损失。

Hystrix 是一个使用命令模式的 Java 类库,每个服务处理请求都有自己的处理器。 所有请求都经过各自的处理器。 处理器记录当前服务的请求失败率。 一旦发现当前服务的请求失败率达到预设值,Hystrix 将拒绝所有后续的服务请求,并返回一个默认结果。 这就是所谓的“熔断”。 一段时间后,Hystrix 将释放服务请求的一部分,并再次计算其请求失败率。 如果此时请求失败率满足预设值,则当前限制开关完全打开;如果请求失败率仍然很高,则拒绝所有服务请求。 这就是所谓的“限流”。 Hystrix 将默认结果直接返回给那些被拒绝的请求,称为“降级” 。

3 热点数据无效

3.1 什么是热点数据故障

我们通常为缓存设置一个过期时间。 过期后,数据将被缓存直接删除,从而在一定程度上保证了数据的实时性。

但是,对于一些请求量非常大的热数据,一旦失效,此时将有大量请求落在数据库上,这可能导致数据库崩溃。 整个过程如下:

如果热点数据失效,那么当再次出现对该数据的查询请求时,它将进入数据库查询。 但是,从请求发送到数据库到数据更新到缓存的时间内,因为数据仍然不在缓存中,在此期间到达的查询请求将落在数据库上,这将给数据库带来巨大压力。 此外,当这些请求查询完成时,缓存将被重复更新。

3.2 解决方案

3.2.1 互斥锁

我们可以使用缓存附带的锁机制。 当第一个数据库查询请求被启动时,缓存中的数据将被锁定;此时,到达缓存的其他查询请求被阻塞将无法查询字段;在数据库查询完成并缓存成功后,锁将被释放;此时,可以直接从缓存中处理其他阻塞的查询请求。

当一个热点数据失效时,只有第一个数据库查询请求被发送到数据库,所有其他的查询请求都被阻塞,从而保护了数据库。 但是,由于使用了互斥锁,其他请求将阻塞等待,系统的吞吐量将下降。 这需要考虑实际的业务,才能实现这一点。

互斥锁可以避免热点数据失效导致的数据库损坏问题。 在实际业务中,经常会出现一批热点数据同时失效的情况。 那么,如何防止这种情况下的数据库过载呢?

3.3.2 设置不同的过期时间

当我们在缓存中存储这些数据时,我们可以错开它们的过期时间。 这可以避免同时发生失效。 例如,在基础时间添加/减去一个随机数,以错开这些缓存的过期时间。

原文

3 major problems and solutions in the cache world

上一篇下一篇

猜你喜欢

热点阅读