在设计和开发高可用系统时,缓存是一个非常重要的考虑因素。缓存一些频繁访问的数据非常有用,这样可以快速访问它们,而且缓存还可以保护下游系统(如数据库)免受过于频繁的访问。
为了在大型系统中提供更好的缓存设计,可能需要首先考虑一些问题。在这篇文章中,我们将讨论一些经常被讨论的缓存问题和缓解计划。
缓存穿透
缓存穿透是指要搜索的数据在数据库中不存在,并且返回的空结果集也没有被缓存,因此每次搜索该键都会最终访问数据库。如果黑客试图通过使用此类键发起大量搜索来发起一些攻击,则底层数据库层将被过于频繁地访问,并可能最终被击垮。
在这种情况下,有一些缓解计划。
- 如果数据库中没有该键的数据,只需返回一个空结果并将其缓存一小段时间(不要设置过长的过期时间)
- 使用 Bloom filter。Bloom filter 类似于 hbase set,可用于检查数据集中是否存在某个键。如果该键存在,则转到缓存层或数据库层;如果该键在数据集中不存在,则直接返回。
如果搜索的键具有较高的重复率,则可以采用第一种解决方案。否则,如果搜索的键具有较低的重复率并且搜索的键太多,则可以采用第二种解决方案来首先过滤掉大多数键。
缓存击穿
缓存击穿是指缓存的数据过期,同时对过期数据进行大量搜索,这突然导致搜索直接访问数据库,并大大增加了数据库层的负载。
这会在高并发环境中发生。通常在这种情况下,需要在搜索的键上加锁,以便当某个线程尝试搜索该键并更新缓存时,其他线程需要等待。缓存更新并释放锁后,其他线程将能够读取新缓存的数据。
另一种可行的方法是通过工作线程异步更新缓存的数据,以便热数据永远不会过期。
缓存雪崩
缓存雪崩是指大量缓存数据同时过期或缓存服务关闭,突然之间,所有对这些数据的搜索都将访问数据库,从而导致数据库层的高负载并影响性能。
为了缓解这个问题,可以采用一些方法。
- 使用集群以确保在任何时间点都有一些缓存服务器实例在运行。如果使用 Redis,则可以拥有 redis 集群。
- 可以配置一些其他方法,如 hystrix 断路器和速率限制,以便底层系统仍然可以服务流量并避免高负载
- 可以调整不同键的过期时间,以便它们不会同时过期。
所有缓解方法都需要根据实际用例和系统设计要求来实现。