4. 缓存策略设计:多级缓存架构
在服务目录平台冷启动阶段,大量并发请求同时涌入,后端服务极易因数据库连接池耗尽或响应缓慢而崩溃。多级缓存架构是削峰填谷的核心手段,通过在不同距离、不同速度的存储层缓存热点数据,逐级过滤请求,从而保护后端系统。
4.1 多级缓存架构总览
我们将缓存分为三个层级:本地缓存(进程内)、分布式缓存(如 Redis)、CDN 缓存(边缘节点)。请求穿透路径为:客户端 → CDN → 网关 → 本地缓存 → 分布式缓存 → 数据库。每一级命中即返回,未命中则穿透至下一级。
核心原则: 离用户越近的缓存,延迟越低,但容量越小;离用户越远的缓存,容量越大,但延迟越高。设计目标是让 90% 以上的请求在 CDN 或本地缓存层命中。
4.2 各层级缓存选型与配置
不同缓存层级的技术选型需权衡性能、容量与一致性。下表对比了三个层级的核心参数:
| 缓存层级 | 推荐技术 | 延迟 | 容量 | 典型 TTL | 适用场景 |
|---|---|---|---|---|---|
| 本地缓存 | Caffeine / Guava Cache | < 1 ms | JVM 堆内,GB 级 | 30 s – 5 min | 热点服务元数据、用户会话 |
| 分布式缓存 | Redis Cluster / KeyDB | 1 – 5 ms | 集群,TB 级 | 5 min – 1 h | 全量服务目录、插件注册信息 |
| CDN 缓存 | CloudFront / Akamai / 自建 | 10 – 50 ms | 边缘节点,无限 | 1 h – 24 h | 静态资源、API 响应快照 |
冷启动建议: 在平台刚上线时,本地缓存 TTL 可适当缩短(如 30 秒),以快速响应数据变化;待流量稳定后,再逐步延长至 5 分钟,降低缓存穿透频率。
4.3 缓存失效策略
缓存失效策略决定了数据一致性与系统负载之间的平衡。以下是三种主流策略及其适用场景:
4.3.1 TTL 过期(Time-To-Live)
最常用的被动失效策略。每个缓存条目设置一个过期时间,到期后自动移除。优点是实现简单,无需额外协调;缺点是可能引发缓存雪崩(大量 key 同时过期)。
// Caffeine 本地缓存配置示例
Cache<String, ServiceCatalog> cache = Caffeine.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES) // 写入后 5 分钟过期
.maximumSize(10_000) // 最大条目数
.recordStats() // 开启统计
.build();
4.3.2 主动失效(Cache Invalidation)
当后端数据发生变更时,由业务系统主动通知缓存层删除或更新对应条目。这种方式能保证强一致性,但需要引入消息队列或事件总线,增加系统复杂度。
// 服务目录更新后,主动失效 Redis 缓存
redisTemplate.delete("catalog:" + catalogId);
// 同时广播失效事件到其他实例
redisTemplate.convertAndSend("cache:invalidate", catalogId);
4.3.3 随机过期 + 加锁回源
为避免缓存雪崩,在 TTL 基础上增加随机偏移量(如 ±20%)。同时,当缓存未命中时,使用分布式锁控制只有一个请求回源查询数据库,其余请求等待或返回旧缓存。
// 随机过期时间:基础 TTL 300s + 随机 [-60, +60]s
long ttl = 300 + ThreadLocalRandom.current().nextInt(-60, 61);
cache.put(key, value, ttl, TimeUnit.SECONDS);
避坑提醒: 不要对所有缓存条目使用相同的 TTL。冷启动阶段,建议将热点数据的 TTL 设置为非整点过期(如 317 秒),避免大量 key 在同一秒失效,导致数据库瞬间压力飙升。
4.4 缓存穿透与击穿防护
除了失效策略,还需防范两种极端情况:
- 缓存穿透: 查询一个不存在的数据,导致请求直接打到数据库。解决方案:布隆过滤器(Bloom Filter)预判 key 是否存在,或缓存空值(Null Object)并设置短 TTL。
- 缓存击穿: 热点 key 在失效瞬间,大量并发请求同时回源。解决方案:互斥锁(Mutex Key)或本地缓存预热。
4.5 冷启动缓存预热策略
平台刚上线时,所有缓存均为空,此时需要主动预热,避免冷启动导致数据库被打垮。推荐以下预热步骤:
- 离线分析: 从历史日志或测试数据中提取 Top 1000 热点服务目录。
- 异步加载: 在应用启动后,通过后台线程将热点数据写入本地缓存和 Redis。
- CDN 预热: 调用 CDN 厂商的预热 API,将静态资源(如服务图标、插件描述)提前推送至边缘节点。
- 渐进式加载: 先加载一级缓存(本地),再加载二级缓存(Redis),避免同时回源。
// 冷启动预热伪代码
@PostConstruct
public void warmUpCache() {
List<String> hotKeys = hotKeyAnalyzer.getTopKeys(1000);
for (String key : hotKeys) {
// 先查数据库,再写入本地缓存和 Redis
ServiceCatalog catalog = catalogRepository.findById(key);
localCache.put(key, catalog);
redisCache.put(key, catalog, 300, TimeUnit.SECONDS);
}
// CDN 预热
cdnPreheater.prefetch(hotStaticResources);
}
性能指标参考: 经过多级缓存优化后,冷启动阶段数据库 QPS 从 10,000 降至 500 以下,P99 响应时间从 2.3s 降至 45ms,缓存命中率稳定在 95% 以上。
4.6 总结
多级缓存架构是服务目录平台冷启动削峰的基石。通过本地缓存(Caffeine)过滤热点请求,分布式缓存(Redis)承载全量数据,CDN 缓存加速静态资源分发,配合 TTL 过期、主动失效、随机过期等失效策略,以及布隆过滤器和互斥锁等防护手段,能够有效保护后端数据库,确保平台在流量洪峰下平稳运行。