## 第1章 多级缓存分层模型:L1本地缓存、L2分布式缓存、L3 CDN、分层边界与数据流 ### 1.1 分层模型定位 多级缓存分层模型将缓存从近端到远端划分为三层:**L1 本地缓存**(进程内/堆内)、**L2 分布式缓存**(Redis / Memcached 集群)、**L3 CDN**(边缘节点 / 静态加速)。每一层在延迟、容量、成本、一致性约束上存在显著差异,通过分层组合实现「近端低延迟、远端大容量、静态全加速」的权衡。 模型核心假设:**大多数请求的热点数据能被前两层命中**,L3主要用于静态资源或长TTL内容,从而控制后端存储(数据库/源站)的压力。 ### 1.2 分层边界定义 | 边界维度 | L1 本地缓存 | L2 分布式缓存 | L3 CDN | |----------|-------------|---------------|--------| | **物理位置** | 应用进程内部(JVM堆 / 堆外) | 独立中间件集群(同机房/跨机房) | 全球边缘节点(PoP) | | **数据规模** | 小(几MB~几十GB) | 中(几十GB~数TB) | 大(TB~PB级静态副本) | | **访问延迟** | < 1ms(微秒级) | 1~5ms(毫秒级) | 10~50ms(跨区域) | | **一致性模型** | 强一致(应用内单线程写入) | 最终一致(主从同步延迟) | 弱一致(TTL过期 + 强制回源) | | **TTL控制** | 短(秒~分钟) | 中等(分钟~小时) | 长(小时~天) | | **成本** | 无额外硬件成本 | 需要独立集群(硬件+网络) | 按流量付费(CDN厂商) | **边界关键规则**: - L1 只缓存**高频访问且数据量小**的热点数据,如用户会话、配置字典。 - L2 缓存**中等规模**的业务聚合数据(如用户主页信息),支持跨进程共享和失效通知。 - L3 缓存**不可变的公共资源**(图片、CSS/JS、视频分片),回源频率极低。 ### 1.3 各层核心职责 | 层级 | 职责 | 典型场景 | |------|------|----------| | **L1 本地缓存** | 消除进程内重复计算,降低L2/数据库QPS | 用户权限缓存、Java反射结果、短时验证码 | | **L2 分布式缓存** | 缓解数据库压力,提供跨节点一致的数据快照 | 商品详情、排行榜、社交动态 | | **L3 CDN** | 卸载源站带宽,降低全球访问延迟 | 静态资源、大文件下载、API响应(若允许) | ### 1.4 数据流与控制流 **数据流(读路径)**: 1. 客户端请求 → 应用服务 → **L1 本地缓存检查**。命中直接返回。 2. L1缺失 → 从 **L2 分布式缓存** 查询。命中返回并回填L1。 3. L2缺失 → 从 **源数据库/业务服务** 查询。结果写入L2和L1后返回。 4. 若请求的是静态资源,应用可绕过L1/L2,直接通过 **L3 CDN** 获取(域名CNAME解析至CDN)。 **控制流(写失效)**: - 写操作发生时,业务服务优先更新数据库,然后通过消息或API向L2发送**失效key指令**;L2失效后,L1因TTL自然过期。 - L3 CDN采用**URL强制刷新**或**版本号变更**实现主动失效(成本较高,尽量依赖TTL)。
核心原则:分层读流程必须遵循「L1 → L2 → 源」的穿透顺序,写操作使用「数据库更新 → 缓存失效」的反向顺序,避免不一致窗口持续扩大。
### 1.5 一致性保障策略 | 一致性场景 | 保障措施 | 容忍度 | |-----------|---------|--------| | L1与L2之间 | 短TTL(15~60s)+ 主动失效广播 | 秒级不一致 | | L2与数据库之间 | 主从同步+缓存旁路(Cache-Aside) | 毫秒~秒级不一致 | | L3与源站之间 | TTL过期回源校验 / 强制刷新 | 分钟~小时级不一致 | **典型风险**:L1未及时收到失效通知导致脏数据蔓延。解决方案:为L1设置最大TTL上界,配合「写入时立即淘汰L1」的钩子(但开销较大,仅对核心数据启用)。 ### 1.6 选型权衡对比 | 对比项 | L1(Caffeine/Ehcache) | L2(Redis集群) | L3(CDN) | |--------|------------------------|-----------------|-----------| | 部署复杂度 | 低(内嵌于应用) | 中(需要集群运维) | 低(服务商托管) | | 动态数据支持 | 强 | 中(需序列化反序列化) | 弱(静态加速为主) | | 成本 | 零 | 高(内存+机器) | 按量付费 | | 弹性伸缩 | 随业务实例自动扩展 | 需预分配slot | 自动全球扩展 | | 故障影响 | 仅影响当前实例 | 影响整个分布式层 | 部分边缘节点失效 | ### 1.7 风险点识别 - **缓存击穿**:热点key在L1/L2同时过期,大量请求直击数据库。对策:L1加互斥锁(但影响性能),L2使用布隆过滤器+热点探测自动延长TTL。 - **缓存雪崩**:大量key同时过期(如同一业务批量失效)。对策:为TTL添加随机偏移量(±10%~±30%)。 - **数据不一致**:L1长时间持有旧值,直到TTL被动过期。对策:对一致性敏感数据(如金融余额)强制走L2且L1 TTL≤5s,或直接跳过L1。 ### 1.8 落地实施步骤 1. **分层决策**:分析各层流量占比,确定哪些数据放入L1(<5%内存)、哪些放入L2(<20%总数据量)、哪些直接走CDN。 2. **读写接口抽象**:定义统一缓存服务接口(`get(key)`, `put(key, value, ttl)`, `delete(key)`),内部实现L1→L2→源的穿透逻辑。 3. **L1配置**:基于应用内存上限设置本地缓存容量(如JVM堆的10%),淘汰策略选择W-TinyLFU(Caffeine)或LRU(Ehcache)。 4. **L2集群初始化**:部署Redis三主三从+Sentinel或Cluster模式,分配最大内存,配置持久化(RDB+AOF)防止重启丢失。 5. **L3 CDN集成**:CNAME域名指向CDN服务商,配置缓存规则(静态资源缓存1h,API响应缓存10s)。 6. **失效机制**:数据库写入后,通过MQ异步发送`cache.invalidate(key, level)`指令,分别淘汰L2和L1(L1通过Redis Pub/Sub或本地失效中心接收)。 7. **监控告警**:接入各层命中率、平均延迟、失效消息积压量指标,阈值告警。 ### 1.9 验收标准 | 验收项 | 目标指标 | 测量方式 | |--------|---------|---------| | L1 命中率 | ≥85%(读请求) | 应用自注册metrics(Prometheus) | | L2 命中率 | ≥90%(L1未命中部分) | Redis info stats(keyspace_hits/keyspace_misses) | | 整体端到端延迟 | P99 < 20ms | APM链路追踪(如SkyWalking) | | 缓存一致性延迟 | 90%写操作在1s内对后续读可见 | 测试工具模拟写后立即读,记录时间差 | | CDN回源率 | <5% | CDN服务商报表 | ### 1.10 本章小结 多级缓存分层模型不是简单的堆叠,而是**根据数据维度、访问模式、一致性级别**进行精细划分。L1解决极端延迟,L2平衡容量与一致性,L3卸载带宽。边界定义、数据流与控制流设计、一致性保障是架构落地的前提。后续章节将深入每一层的选型细节与实战调试方法。 ---
多级缓存分层架构图 客户端 L1 本地缓存(进程内) 延迟 <1ms,容量 MB~GB L2 分布式缓存(Redis集群) 延迟 1~5ms,容量 GB~TB L3 CDN(边缘节点) 延迟 10~50ms,容量 TB~PB 源站/数据库 数据流:客户端→L1→L2→L3→源 控制流:写→数据库→失效L2→L1过期 缓存分层穿透模型
⚠️ 设计关键:必须控制L1的淘汰策略与L2的失效消息之间的时序窗口,避免出现「L1刚淘汰缓存,L2才发出失效消息」导致短期内大量请求直接穿透到数据库。建议为L1与L2之间增加一个「热key预热」阶段,在应用启动或发布时强制填充L1。