第四章 两级缓存的模块分层与组件职责

两级缓存架构的核心不在于缓存算法的先进程度,而在于将缓存能力拆解为四个职责清晰的模块:客户端代理层、L1 管理组件、L2 管理组件和一致性协调器。四者共同构成从请求入口到数据落地的完整链路,任何模块的职责漂移都会导致架构退化。

4.1 架构边界与分层

四个模块按照请求处理顺序与数据温度两个维度进行分层。边界划分的核心原则是:上层无状态,下层有状态;上层依赖下层接口,下层不反向依赖。

  • 客户端代理层——无状态网关,负责协议适配、路由分发、故障屏蔽。不持有任何缓存数据。
  • L1 管理组件——进程内或近端热数据层,容量小(通常 GB 级别),延迟极低(微秒级)。数据生命周期短,由一致性协调器驱动失效。
  • L2 管理组件——分布式冷/温数据层,容量大(TB 级别),延迟在毫秒级。作为 L1 的后备和逐出数据的归宿。
  • 一致性协调器——旁路控制面,不参与数据面 I/O。负责监听数据变更、生成失效事件、协调 L1 与 L2 之间的数据版本。
边界防越原则:客户端代理层不可直连 L2 或数据库;L1 不可自行决定数据持久化;一致性协调器不应承担缓存查询职责。任何越过边界的行为都会破坏分层带来的独立演进能力。
两级缓存模块分层架构 客户端代理层 L1 管理组件 本地堆内缓存 · 微秒级 L2 管理组件 分布式远程缓存 · 毫秒级 一致性协调器 数据源 (DB) 图例 实线:数据流 虚线:控制流(逐出) 箭头:请求/事件方向

4.2 组件职责详解

下表从职责边界、数据载体、核心操作、故障影响四个维度对齐四个组件,防止职责重叠或遗漏。

组件 职责边界 数据载体 核心操作 故障影响范围
客户端代理层 协议适配、请求路由、熔断降级 无状态(不持有缓存数据) 代理转发、健康检测、流量分发 全局请求中断
L1 管理组件 热数据快速查询、本地失效处理 堆内/堆外内存(如 caffeine、ehcache) get/set/evict/失效订阅 仅影响热数据命中率,L2 可兜底
L2 管理组件 全量缓存存储、持久化、逐出策略 分布式 KV 存储(如 redis、memcached) get/set/delete/scan L1 逐出后 miss 会穿透至 DB
一致性协调器 变更监听、失效事件生产、版本仲裁 事件日志或消息队列 publish/subscribe/ack L1 与 L2 数据不一致

4.2.1 客户端代理层

该层通常是 API 网关或 Sidecar 代理。它不关心数据存在 L1 还是 L2,只按照路由规则将请求分发到对应的缓存管理组件。同时负责优雅降级:当 L1 或 L2 连续超时时,自动摘除异常节点并返回降级响应。

4.2.2 L1 管理组件

部署在应用进程内或同一宿主机,使用堆内缓存(如 Caffeine)或堆外内存。核心指标是命中率和失效延迟。L1 必须订阅一致性协调器的失效事件,在收到失效通知后立即驱逐本地条目,不可等待 TTL 自动过期。

4.2.3 L2 管理组件

独立部署的分布式缓存集群,通常使用 Redis Cluster 或 Memcached。L2 的职责是兜底 L1 的 miss 以及接收 L1 逐出的冷数据。L2 需要配置合理的逐出策略(LRU/LFU/TTL),防止内存写满导致频繁的 DB 穿透。

4.2.4 一致性协调器

作为旁路控制面,协调器监听数据变更事件(如 binlog、MQ 消息),生成失效事件并推送至所有 L1 节点。协调器自身不缓存数据,但需要维护事件序号用于版本仲裁。典型实现包括 Redis Pub/Sub、Kafka、自研事件总线。

常见误区:将一致性协调器与 L2 管理组件合并部署。这会破坏控制面与数据面的隔离,导致 L2 集群压力升高时失效事件无法及时投递,进而引发大规模数据不一致。

4.3 数据流与控制流

理解数据流与控制流的分离是掌握两级缓存架构的关键。数据流走 L1 → L2 → DB 路径,控制流走协调器 → L1 路径。

读路径

  1. 客户端请求 → 客户端代理层 → L1 管理组件;
  2. L1 命中 → 直接返回(微秒级);
  3. L1 miss → 转发至 L2 管理组件;
  4. L2 命中 → 返回数据,L1 异步回填(毫秒级);
  5. L2 miss → 穿透至数据源,L2 回填后返回。

写路径

  1. 客户端写入请求 → 客户端代理层 → 一致性协调器;
  2. 协调器生成失效事件并广播至所有 L1 节点;
  3. 协调器将数据写入 L2 管理组件(或委托写入);
  4. L1 节点收到失效事件后驱逐对应条目;
  5. 下次读请求触发 L1 miss → 回填最新数据。

控制流

  • 失效事件流:协调器 → L1 管理组件(异步、至少一次投递);
  • 逐出流:L1 管理组件 → L2 管理组件(同步或异步,取决于逐出策略);
  • 健康探测流:客户端代理层 → L1/L2 管理组件(心跳检测)。

4.4 信任链路

信任链路定义了组件之间的数据可信假设,决定了架构是最终一致性还是强一致性。

信任关系 假设 风险
客户端代理层 → L1/L2 代理层信任 L1/L2 返回的数据是最新版本 若协调器失效,L1 可能返回脏数据
L1 → 一致性协调器 L1 信任协调器推送的失效事件是完整且有序的 事件丢失或乱序导致 L1 残留过期数据
L2 → 数据源 L2 信任数据源为权威数据源 数据源回填延迟时 L2 可能提供旧版本
一致性协调器 → L2 协调器信任 L2 已成功写入后再广播失效 协调器与 L2 之间网络分区导致写后失效未送达
信任链路优化策略:在协调器中引入版本号(version vector),L1 和 L2 各自维护数据的版本戳。读取时比对版本,若 L1 版本低于协调器最新版本则强制 miss,从 L2 或 DB 拉取最新数据。

4.5 选型权衡

决策点 选项 A 选项 B 推荐场景
L1 实现方式 堆内缓存(Caffeine) 堆外内存(OHC) 延迟敏感选堆内;GC 压力大选堆外
L2 存储 Redis Cluster Memcached 需持久化/复杂结构选 Redis;纯 KV 选 Memcached
一致性协调器 Redis Pub/Sub Kafka 小规模选 Redis Pub/Sub;大规模选 Kafka
失效策略 同步失效 异步失效 强一致性选同步;高可用选异步
L1 逐出策略 LRU LFU 热点集中选 LFU;通用场景选 LRU
经验法则:L1 容量设置为应用堆的 5%~10%,L2 容量设置为数据源热数据集的 2~3 倍。一致性协调器使用独立线程池,与业务线程池隔离,防止失效事件阻塞业务请求。

4.6 风险点

  • 协调器单点故障:若协调器宕机,L1 无法接收失效事件,导致脏数据持续服务。必须部署多副本或使用 RAFT 协议保证高可用。
  • 失效事件风暴:批量数据更新时,协调器瞬间产生大量失效事件,可能压垮 L1 节点的事件处理线程。需引入事件合并窗口(如 100ms 内相同 key 的失效事件合并为一次)。
  • L1 与 L2 版本回退:协调器事件乱序导致 L1 用旧版本覆盖新版本。解决方案是为每个 key 附加单调递增版本号,L1 只接受版本号不低于当前值的条目。
  • 客户端代理层过载:代理层同时承担路由和降级逻辑,容易成为 CPU/内存瓶颈。代理层应使用异步非阻塞模型,并独立部署。

4.7 落地步骤

  1. 第 1 步:模块边界对齐——与团队对齐四个组件的职责边界,输出《组件职责矩阵》,明确每个组件的 input、output、fail-mode。
  2. 第 2 步:L2 先行——先部署 L2 管理组件(Redis Cluster),应用直连 L2,验证基础缓存链路。
  3. 第 3 步:引入客户端代理层——在 L2 之前加入代理层,将路由逻辑从应用中剥离,验证代理层的流量分发和故障屏蔽能力。
  4. 第 4 步:L1 局部试点——选择 1~2 个非核心业务接入 L1 管理组件,使用 Caffeine 作为本地缓存,观察命中率和 GC 影响。
  5. 第 5 步:一致性协调器上线——部署协调器,连接 L1 和 L2,验证失效事件的投递延迟和覆盖率。
  6. 第 6 步:全量切换与灰度——逐步将流量从直连 L2 切换到两级缓存链路,每个步骤设置可观测性指标和回滚方案。
落地时机建议:L1 管理组件和一致性协调器属于高风险组件,建议在业务低峰期进行灰度发布,并配置一键降级开关,遇到异常时可快速切回 L2-only 模式。

4.8 验收标准

验收维度 验收指标 目标值
模块隔离性 任一组件宕机不影响其他组件独立运行 L1 宕机时 L2 仍可服务;协调器宕机时 L2 仍可读
职责清晰度 组件间无重叠职责,无跨层调用 代码审计 0 例越权调用
数据一致性 L1 与 L2 数据不一致窗口 ≤ 100ms(异步失效场景)
延迟衰减 L1 命中相比直连 L2 的延迟降低 ≥ 80% 的请求延迟降低 50% 以上
协调器吞吐 单协调器节点支持的失效事件峰值 ≥ 50,000 events/s
故障恢复 协调器主备切换时长 ≤ 5s

完成以上验收标准后,四个模块的分层与职责即达到生产级要求。下一阶段将围绕 L1 与 L2 之间的数据同步策略进行深度设计,确保在写多读少、热点倾斜等极端场景下仍然保持稳定。