2、缓存中间件基础
2.1 缓存的核心概念
缓存(Cache)是位于数据请求路径上的临时存储层,用于保存频繁访问的数据副本。当请求到达时,系统优先从缓存中读取数据,若命中则直接返回,避免每次请求都穿透到后端数据库或上游服务,从而显著降低响应延迟和系统负载。
缓存的核心价值: 将热数据存储在高速介质中,以空间换时间,实现毫秒级甚至微秒级的数据访问。
2.2 缓存命中率
缓存命中率是衡量缓存效率最关键的指标,计算公式如下:
缓存命中率 = 缓存命中次数 / (缓存命中次数 + 缓存未命中次数) × 100%
业界经验参考值:
| 命中率范围 | 健康度评估 | 常见原因 |
|---|---|---|
| ≥ 95% | 优秀 | 缓存策略合理,数据访问局部性强 |
| 80% ~ 95% | 良好 | 大部分热数据被缓存,仍有优化空间 |
| 60% ~ 80% | 一般 | 缓存容量不足或过期策略过于激进 |
| < 60% | 较差 | 缓存设计存在严重问题,需重新评估 |
优化建议: 在 API Gateway 中,应针对不同路由设置独立的缓存 TTL(Time To Live),并监控每个缓存键的命中率,及时调整缓存策略。
2.3 缓存三大经典问题
在缓存使用过程中,必须警惕以下三种异常场景,它们会直接导致后端系统压力骤增甚至崩溃。
2.3.1 缓存穿透
定义: 请求查询一个缓存和数据库中都不存在的数据(如查询一个不存在的用户ID),导致每次请求都直接打到数据库。
解决方案:
- 缓存空值: 将查询结果为 null 的 key 也缓存起来,设置较短的 TTL(如 30~60 秒)。
- 布隆过滤器: 在缓存层之前加一层布隆过滤器,快速判断 key 是否可能存在。
2.3.2 缓存击穿
定义: 某个热点 key 在缓存过期的瞬间,大量并发请求同时访问该 key,导致所有请求都穿透到数据库。
解决方案:
- 互斥锁: 只允许一个线程去数据库加载数据,其他线程等待。
- 逻辑过期: 在 value 中存储过期时间,异步刷新缓存。
2.3.3 缓存雪崩
定义: 大量缓存 key 在同一时间过期失效,或缓存节点宕机,导致海量请求直接涌入数据库。
解决方案:
- 过期时间随机化: 在基础 TTL 上增加随机偏移量(如 ±10%)。
- 缓存高可用: 采用 Redis 哨兵或集群模式,避免单点故障。
- 限流降级: 在 API Gateway 层对数据库请求进行限流保护。
2.4 常见缓存中间件对比
在 API Gateway 场景中,最常用的缓存中间件包括 Redis、Memcached 以及本地缓存(如 Guava Cache、Caffeine)。以下是核心对比:
| 对比维度 | Redis | Memcached | 本地缓存 |
|---|---|---|---|
| 数据结构 | 丰富(String、Hash、List、Set、Sorted Set、Bitmap、HyperLogLog 等) | 仅支持 String(键值对) | 通常为键值对,支持对象缓存 |
| 持久化 | 支持 RDB 和 AOF 持久化 | 不支持持久化 | 无持久化,随进程销毁 |
| 高可用 | 主从、哨兵、集群模式 | 客户端分片,无原生高可用 | 单机,无高可用 |
| 性能(读/写) | 单机 10万+ QPS | 单机 10万+ QPS | 纳秒级,远高于网络缓存 |
| 分布式能力 | 原生集群,自动分片 | 客户端一致性哈希 | 不支持分布式 |
| 内存管理 | 可配置淘汰策略(LRU、LFU、TTL 等) | LRU 淘汰,内存池管理 | 依赖 JVM 堆内存,需手动控制 |
| 典型应用场景 | API 响应缓存、Session 共享、分布式锁、排行榜、计数器 | 简单键值缓存、Session 存储 | 热点数据本地缓存、配置缓存、元数据缓存 |
架构选型建议: 在 API Gateway 中,推荐采用 本地缓存 + Redis 分布式缓存 的两级缓存架构。本地缓存用于存储最热门的元数据(如路由规则、限流配置),Redis 用于存储用户维度的业务缓存数据,兼顾性能与一致性。
2.5 两级缓存架构示意
下图展示了 API Gateway 中常见的两级缓存部署模式:
实践要点: 在 API Gateway 中,本地缓存建议存储路由规则、限流配额、API 元数据等变化频率极低的数据;Redis 缓存存储用户会话、接口响应快照、计数器等需要跨节点共享的数据。两级缓存之间需通过消息总线或 Redis Pub/Sub 实现失效通知,保证数据最终一致性。