前言
虽然缓存被认为是软件系统的性能助推器,但如果处理不当,它也容易出现错误。
在本文中,我将介绍 3 个有时可能会造成灾难性后果的常见缓存问题,希望大家在架构上引入缓存时,需要考虑到。
缓存击穿
缓存故障
当缓存键过期时会发生缓存故障,并且多个请求同时访问数据库以查找相同的键。
让我们来看看它是如何工作的:
- 热缓存键过期。
- 多个并发请求进来搜索同一个键。
- 服务器向数据库发起多个并发请求以查找相同的键。
缓存击穿会显著增加数据库的负载,尤其是当许多热键同时过期时。
下面是解决这个问题的2种解决方案:
- 获取搜索到的key的分布式锁,当一个线程试图更新缓存时,其他线程需要等待。
- 利用
Refresh-ahead
策略异步刷新热数据,使热键永不过期。
缓存穿透
缓存穿透
当搜索到的key
既不在缓存中, 也不在数据库中时,就会发生缓存穿透, 连数据库都穿透过去了。
让我们来看看它是如何工作的,当key
既不在缓存中也不在数据库中时,就会发生这种情况。
- 当用户查询
key
时,应用程序由于缓存未命中而去查询数据库数据库。 - 由于数据库不包含该
key
并返回空结果,因此该key
也不会被缓存。 - 因此,每个查询最终都会导致缓存未命中,而命中数据库,直接进行查库。
虽然乍一看这似乎微不足道,但攻击者可以通过使用此类密钥启动大量搜索来尝试破坏你的数据库。
为了解决这个问题,我们可以:
- 缓存过期时间较短的空结果。
- 使用布隆过滤器。在查询数据库之前,应用程序在布隆过滤器中查找
key
,如果key
不存在则立即返回。
缓存雪崩
当对数据库的请求突然激增时,就会发生缓存雪崩。
这发生在:
- 许多缓存数据同时过期。
- 缓存服务宕机,所有请求都直接访问数据库。
数据库流量的突然激增可能会导致级联效应,并可能最终导致您的服务崩溃。
下面是一些常见的解决方案:
- 调整缓存键的过期时间,使它们不会同时过期。
- 使用Refresh-ahead 策略异步刷新热数据,使其永不过期。
- 使用缓存集群来避免单点故障。当主节点崩溃时,其中一个副本被提升为新的主节点。
总结
虽然这些缓存问题起初看起来微不足道,但有时可能会对我们的下游客户端和依赖项产生级联效应。事先了解它们可以让我们设计一个更强大的系统,也可以简化我们的故障排除过程。