当一家全球支付平台每天处理上亿次API调用时,毫秒级的缓存响应和零数据丢失同样重要。他们通过精细化的数据存储策略,在Apigee Hybrid中实现了99.99%的API可用性,同时将平均响应时间控制在50毫秒内。
Redis vs Cassandra:性能与持久性的精准平衡
Apigee Hybrid 的数据存储架构遵循一个核心原则:热数据进Redis,冷数据入Cassandra。但这里的"热"与"冷"并非简单的时间划分,而是基于数据的访问模式、一致性要求和生存周期精心设计的。
1. Redis缓存层:毫秒级响应的保障
Redis在Apigee中担任高性能读写缓存的角色,所有需要极速访问但又可以容忍短暂不一致的数据都在这里。
1.1 Redis存储的具体数据类型
| 数据类型 | 具体结构 | 缓存策略 | 过期时间 | 刷新机制 |
|---|---|---|---|---|
| API密钥与凭证 | 键:org:{org}:env:{env}:apikey:{hash} 值:客户端ID、产品列表、状态等 |
读写穿透 | 300秒(TTL) | Cassandra变更时失效 |
| OAuth 2.0令牌 | 键:oauth2:access_token:{hash} 值:客户端ID、用户ID、作用域等 |
写入时缓存 | 令牌有效期+5分钟 | 令牌刷新时更新 |
| KVM键值映射 | 键:kvm:env:{env}:map:{map_name}:{key} 值:字符串/JSON数据 |
懒加载 | 300秒(TTL) | 显式清除或过期 |
| 响应缓存 | 键:response_cache:proxy:{proxy}:{hash} 值:状态码、头部、响应体 |
策略控制 | 策略定义(如300秒) | 请求参数变化时刷新 |
| 策略配置 | 键:proxy:{name}:revision:{rev}:policies 值:策略配置对象 |
全量缓存 | 代理重新部署时 | 配置同步时更新 |
1.2 Redis的缓存架构策略
Apigee Hybrid 实现了两级缓存架构,将性能优化发挥到极致:
yaml
# Apigee缓存层级示例配置
缓存层级:
第一级(L1):
位置: MessageProcessor本地内存
容量: 10,000条记录(可配置)
算法: LRU(最近最少使用)
命中率: ~60-70%的热数据
第二级(L2):
位置: Redis集群(分布式)
容量: 按需扩展(通常2-8GB)
算法: 所有键LRU
命中率: ~20-30%的温数据
穿透到数据库:
数据源: Cassandra持久化存储
延迟: 5-15毫秒
占比: ~10-20%的冷数据
这种架构的关键优势在于:约90%的数据请求在前两级缓存得到满足,只有不到10%需要访问Cassandra,这直接将平均响应时间从数百毫秒降低到数十毫秒。
2. Cassandra持久层:数据安全的最终防线
Cassandra存储所有不可丢失、需要强一致性或长期保存 的数据,是系统的单一事实来源(Single Source of Truth)。
2.1 Cassandra表结构设计
2.1.1 配额计数器表
sql
CREATE TABLE quota_counters (
organization text,
environment text,
proxy_name text,
policy_name text,
identifier text, -- 格式:"apiKey:client123" 或 "ip:192.168.1.1"
time_bucket timestamp, -- 时间窗口:如每分钟起始时间
counter_value counter, -- Cassandra原生计数器类型
PRIMARY KEY ((organization, environment, proxy_name, policy_name, identifier), time_bucket)
) WITH CLUSTERING ORDER BY (time_bucket DESC)
AND default_time_to_live = 604800; -- 7天TTL,自动清理旧数据
使用场景:
- Quota策略:分布式配额计数
- Spike Arrest策略:滑动窗口限流
- 使用Cassandra原生counter类型,确保原子递增操作
2.1.2 OAuth 2.0完整令牌表
sql
CREATE TABLE oauth_tokens (
token_type text, -- 'access_token' 或 'refresh_token'
token_hash text, -- SHA256哈希值
client_id text,
user_id text,
scopes set<text>, -- 令牌权限范围
created_at timestamp,
expires_at timestamp,
metadata map<text, text>, -- 自定义元数据
previous_token_hash text, -- 令牌轮换链
PRIMARY KEY ((token_type, token_hash))
) WITH gc_grace_seconds = 864000
AND compaction = {'class': 'TimeWindowCompactionStrategy'};
2.1.3 API密钥权威存储
sql
CREATE TABLE developer_app_credentials (
organization text,
developer_email text,
app_id text,
consumer_key text, -- API密钥
consumer_secret text, -- 加密存储
api_products set<text>,
status text, -- 'approved', 'revoked', 'pending'
attributes map<text, text>,
created_at timestamp,
last_modified_at timestamp,
PRIMARY KEY ((organization, developer_email, app_id), consumer_key)
);
2.2 Cassandra一致性保证
对于不同的操作类型,Apigee采用不同的一致性级别:
| 操作类型 | 一致性级别 | 说明 | 延迟影响 |
|---|---|---|---|
| 配额计数写入 | LOCAL_QUORUM | 确保本地数据中心多数副本确认 | 中(10-30ms) |
| 令牌验证读取 | LOCAL_ONE | 只需一个本地副本响应 | 低(5-15ms) |
| API密钥读取 | LOCAL_QUORUM | 保证读取最新数据 | 中(10-30ms) |
| KVM写入 | LOCAL_QUORUM | 配置数据需要强一致性 | 中(10-30ms) |
3. 缓存策略:何时使用Redis,何时使用Cassandra
3.1 明确的分工原则
必须使用Cassandra存储的数据:
- 所有需要持久化且不能丢失的数据
- 需要在多个MP实例间保持强一致性的数据
- 作为Redis缓存回填的数据源
- 长期历史数据和分析数据
适合Redis缓存的数据:
- 高频读取但低频更新的数据
- 可以容忍短暂不一致的数据
- 需要极低访问延迟的数据
- 有明确失效策略的临时数据
3.2 实际场景中的数据流向
场景:API密钥验证流程
java
public ApiKeyValidationResult validateApiKey(String apiKeyHash, String organization, String environment) {
// 第1步:检查L1缓存(MP本地内存)
String l1Key = "local:apikey:" + apiKeyHash;
ApiKeyValidationResult l1Result = localCache.get(l1Key);
if (l1Result != null) {
metrics.cacheHit("L1");
return l1Result;
}
// 第2步:检查L2缓存(Redis)
String redisKey = String.format("org:%s:env:%s:apikey:%s",
organization, environment, apiKeyHash);
ApiKeyValidationResult redisResult = redisClient.get(redisKey);
if (redisResult != null) {
// 回填L1缓存
localCache.put(l1Key, redisResult, LOCAL_CACHE_TTL);
metrics.cacheHit("L2");
return redisResult;
}
// 第3步:查询Cassandra(权威数据源)
Statement query = QueryBuilder.select()
.from("developer_app_credentials")
.where(QueryBuilder.eq("consumer_key_hash", apiKeyHash));
ResultSet resultSet = cassandraSession.execute(query);
Row row = resultSet.one();
if (row == null) {
return ApiKeyValidationResult.invalid("Key not found");
}
ApiKeyValidationResult dbResult = mapRowToValidationResult(row);
// 第4步:异步写入缓存(不阻塞响应)
CompletableFuture.runAsync(() -> {
// 写入Redis,TTL=300秒
redisClient.setex(redisKey, 300, serialize(dbResult));
// 写入本地缓存,TTL=60秒
localCache.put(l1Key, dbResult, 60);
});
metrics.cacheMiss();
return dbResult;
}
4. 高级缓存策略与优化技术
4.1 缓存预热机制
Apigee Hybrid在以下时机自动执行缓存预热:
- MP实例启动时:加载活跃API密钥和常用配置
- 策略部署后:预热相关策略配置
- 定期任务:每天凌晨预热高频访问数据
bash
# 手动触发缓存预热的API示例
curl -X POST \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
"https://apigee.googleapis.com/v1/organizations/{org}/environments/{env}/cache:预热\
?type=apikeys&limit=10000"
4.2 缓存失效策略
| 失效触发条件 | 影响范围 | 执行机制 |
|---|---|---|
| API密钥吊销 | 单个密钥 | 立即从Redis删除,Cassandra标记状态 |
| OAuth令牌过期 | 单个令牌 | TTL自动过期,后台清理作业 |
| KVM值更新 | 单个键 | 清除Redis对应键,更新Cassandra |
| 代理重新部署 | 相关配置 | 清除所有相关策略缓存 |
| 计划内维护 | 全部/部分 | 批量清除,然后逐步重新填充 |
4.3 监控与告警指标
有效的缓存策略需要完善的监控:
yaml
# Prometheus监控指标示例
监控指标:
redis_hit_rate:
计算: rate(redis_cache_hits_total[5m]) /
(rate(redis_cache_hits_total[5m]) + rate(redis_cache_misses_total[5m]))
告警阈值: < 0.85 (命中率低于85%)
cache_响应延迟:
百分位: P50 < 2ms, P95 < 5ms, P99 < 10ms
数据源: redis_command_duration_seconds_bucket
cassandra_查询延迟:
百分位: P95 < 20ms (读取), P95 < 50ms (写入)
数据源: cassandra_client_request_latency_seconds
内存使用率:
redis_memory_used_bytes / redis_memory_max_bytes
告警阈值: > 0.8 (80%使用率)
5. 性能对比与容量规划
5.1 性能基准数据
| 操作类型 | Redis访问 | Cassandra访问 | 性能差异 |
|---|---|---|---|
| API密钥验证 | 0.5-2ms | 5-15ms | 10-30倍 |
| OAuth令牌验证 | 0.5-2ms | 5-20ms | 10-40倍 |
| 配额检查 | 1-3ms | 10-30ms | 10倍 |
| KVM读取 | 0.5-2ms | 5-15ms | 10-30倍 |
| 响应缓存读取 | 0.5-1ms | N/A | 仅Redis |
5.2 容量规划建议
Redis容量计算
总内存需求 = 各数据类型内存之和
API密钥存储需求 = 活跃密钥数 × 平均密钥大小(500B) × 副本因子(1.5)
OAuth令牌需求 = 并发令牌数 × 平均令牌大小(1KB) × 副本因子(1.5)
KVM存储需求 = KVM条目数 × 平均条目大小(2KB)
响应缓存需求 = 缓存响应数 × 平均响应大小(10KB)
示例:支持100万活跃API密钥的Redis集群
API密钥:1,000,000 × 500B × 1.5 = 750MB
OAuth令牌:100,000 × 1KB × 1.5 = 150MB
KVM:10,000 × 2KB = 20MB
响应缓存:5,000 × 10KB = 50MB
总需求:≈ 1GB + 30%缓冲 = 1.3GB内存
Cassandra容量计算
Cassandra需求 = 数据保留周期 × 每日增长量
配额计数器:7天 × 1000万次/天 × 100B = 7GB
OAuth令牌:90天 × 10万令牌/天 × 2KB = 18GB
API密钥历史:永久 × 变化频率 = 较小
总需求:≈ 25GB + 50%副本/索引 = 37.5GB
6. 故障场景与恢复策略
6.1 Redis故障场景
场景:Redis主节点故障
影响:缓存命中率降为0,所有请求穿透到Cassandra
系统响应:自动故障转移到从节点,MP短暂降级
恢复时间:30-60秒(K8s Pod重启)
数据影响:可接受的数据丢失(缓存数据)
6.2 Cassandra故障场景
场景:一个Cassandra节点故障
影响:该节点负责的部分数据暂时不可用
系统响应:自动使用其他副本,写入使用hinted handoff
恢复:节点恢复后自动同步数据
数据影响:无数据丢失(最终一致)
6.3 缓存不一致处理
java
// 缓存一致性验证与修复机制
public class CacheConsistencyChecker {
@Scheduled(fixedDelay = 300000) // 每5分钟运行一次
public void validateCacheConsistency() {
// 1. 抽样检查Redis与Cassandra一致性
List<String> sampleKeys = redisClient.randomKeys(100);
for (String key : sampleKeys) {
Object redisValue = redisClient.get(key);
Object cassandraValue = fetchFromCassandra(extractIdFromKey(key));
if (!Objects.equals(redisValue, cassandraValue)) {
// 记录不一致
metrics.increment("cache.inconsistencies");
// 根据策略修复
if (shouldPreferCassandra(key)) {
redisClient.set(key, cassandraValue);
}
}
}
}
}
7. 最佳实践总结
- 缓存粒度控制:避免缓存大对象,采用分片或压缩技术
- TTL分层设置:根据数据变更频率设置不同的过期时间
- 监控驱动优化:基于实际命中率调整缓存策略
- 容量前瞻规划:预留30-50%的缓冲空间应对峰值
- 定期一致性检查:确保缓存与源数据同步
- 故障演练:定期模拟缓存和数据库故障,验证系统韧性
通过这样精细化的数据存储设计,Apigee Hybrid能够在保证数据一致性和持久性的同时,提供接近纯内存系统的性能表现,这正是它能够支撑企业级关键业务API的核心技术基础。