核心原则:分层读流程必须遵循「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的淘汰策略与L2的失效消息之间的时序窗口,避免出现「L1刚淘汰缓存,L2才发出失效消息」导致短期内大量请求直接穿透到数据库。建议为L1与L2之间增加一个「热key预热」阶段,在应用启动或发布时强制填充L1。