Single-Flight 缓存:如何防止单个热点 Key 拖垮数据库

Single-Flight 缓存:如何防止单个热点 Key 拖垮数据库

一种在缓存过期期间管理数据库负载的解决方案。

当网站面临高流量时,可能会引发性能问题,尤其是在依赖数据库进行数据检索的情况下。我们将探讨许多应用程序面临的一个常见问题的解决方案:如何在不让数据库过载的情况下处理缓存过期。

了解问题所在

在网站 podbor-minuta.ru 上,某些数据库查询相当繁重。为了加快响应速度,他们将结果保存在缓存中。例如,他们会缓存房源列表以及列表页面的聚合数据。当缓存有效时,一切运行顺畅。然而,麻烦始于缓存过期之时。

有一天,在高负载期间,该网站遇到了一个严重问题。一批 500 个响应发生超时。尽管数据库仍在运行,但查询开始排队堆积。出现这种情况是因为多个请求同时尝试访问相同的数据,从而导致了瓶颈。

什么是缓存踩踏?

最初的缓存逻辑很简单:如果某个值不在缓存中,应用程序就会查询数据库来计算它,然后将该值存回缓存中。这对于单个请求运作良好。但想象一下,如果缓存过期,突然涌入两百个请求获取相同的数据。每个请求都发现缓存为空,并且每个请求都同时查询数据库。这种情况被称为 缓存踩踏.

在缓存踩踏期间,数据库会被大量相同的繁重查询压垮,导致达到连接数上限。结果,一些请求因超时而被丢弃。

实现 Single-Flight 缓存

为了解决这个问题,团队实现了一种称为 Single-Flight 缓存的技术。该方法确保如果针对某个特定 Key 的计算已经在运行,针对该相同 Key 的任何其他请求都将等待该结果,而不是发起各自的数据库查询。以下是它的工作原理:

步骤 1:检查缓存

首先,应用程序检查该值是否已在缓存中。如果存在,则立即返回该值。

步骤 2:处理缓存未命中

如果该值不在缓存中,它会检查针对该 Key 的计算是否已在进行中。如果是,应用程序将等待该计算完成并返回结果。

步骤 3:创建 Promise

如果当前没有正在进行的计算,应用程序就会启动一个计算。它为该计算创建一个 Promise,并使用该 Key 将其存储在 Map 中。这样,其他请求就可以访问同一个 Promise,而不是启动新的数据库查询。

以下是展示其工作原理的简化代码示例:

const inFlight = new Map<string, Promise<unknown>>();

async function getOrSet<T>(key: string, compute: () => Promise<T>): Promise<T> {
    const cached = cache.get(key);
    if (cached !== undefined) return cached as T;

    const running = inFlight.get(key);
    if (running) return running as Promise<T>;

    const promise = compute().then((value) => {
        cache.set(key, value);
        return value;
    }).finally(() => {
        inFlight.delete(key);
    });

    inFlight.set(key, promise);
    return promise;
}

Single-Flight 缓存的优势

实现 Single-Flight 缓存带来了诸多好处:

  • 减轻数据库负载:仅有一个查询访问数据库,而不是两百个相同的查询,从而显著减轻了负载。
  • 提升性能:连接池不再被无谓地耗尽,从而在高流量期间实现更平稳的性能。
  • 高效性:缓存命中路径依然迅速,对于在缓存中找到数据的请求没有任何额外开销。

结论

Single-Flight 缓存是一种在高峰期管理数据库负载的有效策略,尤其是在缓存发生过期时。通过确保同一时间只有一个请求处理繁重的查询,应用程序可以保持性能稳定并防止服务器崩溃。

优点

  • 防止缓存过期期间数据库过载。
  • 保持缓存数据的快速响应时间。
  • 减少重复数据库查询的数量。

缺点

  • 需要额外的逻辑来管理进行中的请求。
  • 在更严格的环境中可能需要共享锁,从而增加复杂性。

注意事项

本文仅供学习参考。如果您要实现任何缓存策略,请务必使用实际数据替换占位符值,并在依赖相关结论前对照原始资料进行核实。

常见问题

  • 什么是 Single-Flight 缓存? —— 这是一种缓存策略,允许同一时间仅有一个请求为特定 Key 获取数据,从而防止产生多个数据库查询。
  • 什么是缓存踩踏? —— 指在缓存未命中后,针对相同数据的多个请求同时冲击数据库,从而使数据库不堪重负的情况。
  • 缓存如何提升性能? —— 缓存将频繁访问的数据存储在内存中,与查询数据库相比可以实现更快的数据检索。
  • 缓存过期时会发生什么? —— 当缓存过期时,请求可能会大量涌入数据库以获取相同的数据,如果处理不当会导致性能问题。
  • Single-Flight 缓存可以用于多台服务器吗? —— 可以,但需要诸如共享锁之类的附加机制来有效管理跨不同服务器的请求。
  • 缓存有什么好处? —— 缓存可以减轻数据库负载,加快数据检索速度,并提高应用程序的整体性能。

标签

#caching #database #performance #webdev #architecture #softwaredevelopment #programming #tech

Free field guide

API Security Testing Checklist

A practical workflow for testing authentication, authorization, input handling, business logic, and evidence without losing track of scope.