缓存概念与原理

缓存(Cache)是计算机系统中用于临时存储数据副本的高速存储层。在 API 系统中,缓存位于客户端与原始数据源之间,通过保存频繁访问的数据来减少对后端服务的请求压力,从而降低响应延迟、提升系统吞吐量。

核心思想:用空间换时间——用有限的存储空间换取更快的访问速度。

2.1 缓存的定义与工作原理

缓存本质上是一个键值存储系统。当请求到达时,系统首先检查缓存中是否存在所需数据;如果存在,则直接返回缓存数据(缓存命中);如果不存在,则从原始数据源获取数据,同时将数据副本写入缓存(缓存未命中)。

缓存请求处理流程 客户端 缓存层 原始数据源 ① 请求 ② 命中 → 直接返回 ③ 未命中 → 回源 ④ 返回数据并写入缓存 命中? 是 否 回源查询

2.2 缓存命中与未命中

缓存系统的性能主要由两个指标衡量:命中率和未命中率。

术语 定义 影响
缓存命中 请求的数据在缓存中存在,直接返回 响应时间极短(微秒级),无后端压力
缓存未命中 请求的数据在缓存中不存在,需回源查询 响应时间增加(毫秒级),消耗后端资源
缓存穿透 请求的数据既不在缓存也不在数据源中 每次请求都穿透到后端,可能导致雪崩
缓存击穿 热点 key 在过期瞬间被大量并发请求 瞬间高并发冲击后端数据库
缓存雪崩 大量缓存同时过期,导致请求全部落到后端 后端负载骤增,可能引发系统崩溃
实践建议:API 缓存设计应追求 90% 以上的命中率。命中率低于 80% 时,需要检查缓存键设计、过期策略或数据访问模式是否合理。

2.3 缓存淘汰策略

缓存空间有限,当缓存写满时,必须按照特定规则移除旧数据,为新数据腾出空间。这些规则称为淘汰策略。以下是三种最常用的策略:

2.3.1 LRU(最近最少使用)

LRU 淘汰最长时间未被访问的数据。其核心假设是:如果数据最近被访问过,那么未来被访问的概率也更高。

  • 实现方式:双向链表 + 哈希表
  • 时间复杂度:O(1) 访问和淘汰
  • 适用场景:大多数通用缓存场景,如浏览器缓存、API 响应缓存
  • 缺点:偶发性的批量访问可能淘汰真正热点的数据

2.3.2 LFU(最不经常使用)

LFU 淘汰访问频率最低的数据。其核心假设是:访问频率低的数据未来被访问的概率也低。

  • 实现方式:最小堆或多级链表(按频率分组)
  • 时间复杂度:O(log n) 或 O(1)
  • 适用场景:访问模式稳定的场景,如 CDN 缓存、热门内容缓存
  • 缺点:新数据刚加入时频率低,容易被淘汰(冷启动问题)

2.3.3 FIFO(先进先出)

FIFO 淘汰最早进入缓存的数据。其核心假设是:先来的数据已经完成了它的使命。

  • 实现方式:队列
  • 时间复杂度:O(1)
  • 适用场景:数据时效性强的场景,如会话缓存、日志缓存
  • 缺点:不考虑访问频率和访问时间,可能淘汰热点数据
三种缓存淘汰策略对比 LRU 最近最少使用 淘汰最久未访问的数据 A(旧) B C D(新) ← 淘汰方向 访问 D → D 移到尾部 新数据 E → 淘汰 A LFU 最不经常使用 淘汰访问频率最低的数据 频率 5:A 频率 3:B、C 频率 1:D(淘汰) ← 淘汰频率最低的 D 新数据 E → 频率 1 若频率相同 → 淘汰最旧的 FIFO 先进先出 淘汰最早进入缓存的数据 A(最早)→ 淘汰 B C D(最新) ← 淘汰队首 A

2.4 策略对比与选型

策略 淘汰依据 时间复杂度 内存开销 适用场景
LRU 最近访问时间 O(1) 中(链表 + 哈希表) 通用 API 缓存、浏览器缓存
LFU 访问频率 O(log n) ~ O(1) 高(频率计数器) CDN 缓存、热门数据缓存
FIFO 进入时间 O(1) 低(队列) 会话缓存、临时数据缓存
选型注意:没有万能的淘汰策略。LRU 是大多数场景的默认选择;如果数据访问模式有明显的“热点”特征(如 20% 的数据占 80% 的访问),LFU 更优;如果数据有明确的过期时间或生命周期,FIFO 更简单高效。实际生产环境中,Redis 等缓存中间件通常支持多种策略配置,建议根据业务特征进行压测后确定。

2.5 缓存策略在 OpenAPI 中的体现

在 OpenAPI 规范中,缓存策略通过 Cache-Control 响应头字段与 API 契约结合。以下是一个典型的缓存配置示例:

paths:
  /api/users:
    get:
      summary: 获取用户列表
      responses:
        '200':
          description: 成功返回用户列表
          headers:
            Cache-Control:
              schema:
                type: string
                example: "public, max-age=3600, s-maxage=7200"
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/User'

上述配置中:

  • max-age=3600:客户端缓存 1 小时
  • s-maxage=7200:共享缓存(如 CDN)缓存 2 小时
  • public:允许任何中间缓存存储响应
课程关联:第 5 章将深入讲解如何在 OpenAPI 契约中精确描述缓存策略,包括 ETag、Last-Modified 和条件请求的契约定义。