转载 | 5 分钟内解释每种缓存策略
Cache-Aside (Lazy Loading)
这可以说是你最常遇到的一种方式。使用 Cache-Aside 模式时,应用程序代码直接负责管理缓存。当需要数据时,应用程序会先检查缓存中是否存在该数据。如果缓存命中(cache hit),数据会立即返回;如果缓存未命中(cache miss),应用程序会从主数据源(比如数据库)获取数据,将数据副本存入缓存以备下次使用,然后再返回数据。
举个例子,假设要获取某个用户的个人主页:应用程序先检查缓存中是否有 user:123 的数据。如果没有,它会查询数据库、将结果存入缓存 key 为 user:123 的位置,然后再继续后续操作。
适用场景:
- 你的工作负载主要是以读操作为主。
- 偶尔出现过期数据是可以接受的(例如数据库发生了变化,但没有做缓存失效处理)。
- 你更倾向于在应用程序中保持缓存交互逻辑的简单性。
Read-Through
在 Read-Through 策略中,应用程序在读取数据时只与缓存交互,将缓存视为主要的数据源。背后的“魔法”是在于:如果请求的数据不在缓存中(即缓存未命中),缓存系统会负责从底层数据库获取数据,存储数据,然后再返回给应用程序。这大大简化了应用程序代码,因为它不需要专门编写从数据库读取数据的逻辑。
举个例子,假设有一个产品目录服务,使用了带有 CacheLoader 的缓存库。应用程序只需调用 cache.get("product:xyz"),缓存系统会在缓存未命中的时候自动处理与数据库的交互。
适用场景:
- 你的工作负载主要是以读取操作为主。
- 你希望将数据获取逻辑从主应用流程中抽象出来。
- 你选用的缓存提供方(比如某些库或托管服务)明确支持这种自动数据加载功能。
Write-Through
在 Write-Through 策略中,一致性至关重要。当你的应用需要写入或更新数据时,它会同时在两个地方操作:缓存和数据库。只有当这两个存储都成功确认写入后,操作才被视为完成。这保证了缓存始终与数据库保持一致,减少了返回过期数据的可能性。
一个典型的例子是关键更新,比如更改用户的电子邮件地址。应用会确保新邮箱同时保存到缓存和数据库,然后再确认操作成功。其权衡是写入延迟可能会更高,因为需要等待两次操作完成。
适用场景:
- 当数据一致性至关重要,不能容忍缓存与数据库之间存在差异,并且可以接受稍微降低写入性能作为权衡时,使用该策略。
Write-Behind (Write-Back)
需要极快的写入速度?Write-Behind 可能是你的答案。在这种策略下,应用程序只写入缓存,缓存几乎立即确认写入操作。之后缓存负责将数据异步写回数据库,通常是在短暂延迟后或通过批量写入的方式完成。这大大提升了从应用角度看写入的性能。
这种方式非常适合高频率更新的场景,比如页面浏览计数器、社交媒体的“点赞”,或实时游戏分数等对速度要求极高的应用。不过,这也存在风险:如果缓存未将数据持久化到数据库之前发生故障,数据可能会丢失。
适用场景:
- 当写入性能是首要考虑,应用会产生突发写入,并且能够容忍在异步写入完成前缓存发生故障时可能导致的少量数据丢失风险。
Write-Around
有时,在写入操作中涉及缓存是不必要的,甚至是有害的。Write-Around 策略通过让应用程序直接将数据写入数据库,完全绕过缓存来解决这个问题。数据只有在后续被读取时(通常使用 Cache-Aside 模式进行读操作)才会进入缓存。
比如批量数据导入或密集日志记录。将这些数据直接写入数据库可以避免向缓存灌入可能很少或不会立即访问的信息,从而让缓存更专注于更“热”、更相关的数据。
适用场景:
- 当你有写入量大的工作负载,且数据在写入后不太可能被立即读取,同时你希望避免用可能的“冷”数据污染缓存时,适合使用该策略。