Amazon Pilot 采用 Redis 作为主要缓存存储,实现了基于产品维度的分层缓存策略。本文档详细说明了缓存架构设计、键命名规范、缓存策略以及最佳实践。
- Redis: 主缓存存储,支持高并发读写
- Go-Redis: Redis Go 客户端库
- 统一Key管理: 集中管理缓存键生成逻辑
- 按产品缓存: 以产品为单位进行缓存,提高多用户场景下的缓存命中率
- 分层缓存: 不同类型数据采用不同的TTL策略
- 统一Key管理: 集中定义缓存键,避免硬编码
- 缓存一致性: 数据更新时及时清理相关缓存
amazon_pilot:{category}:{identifier}
缓存Key统一在 internal/pkg/cache/keys.go 中定义:
// 产品完整数据缓存(新策略)
ProductDataPrefix = "amazon_pilot:product_data:"
// 产品基本信息缓存
ProductCachePrefix = "amazon_pilot:product:"
// 产品价格缓存
ProductPricePrefix = "amazon_pilot:product_price:"
PriceCachePrefix = "amazon_pilot:price:"
// 产品排名缓存
ProductRankingPrefix = "amazon_pilot:product_ranking:"
RankingCachePrefix = "amazon_pilot:ranking:"// 用户追踪列表缓存(已弃用,改为按产品缓存)
UserTrackedPrefix = "amazon_pilot:user_tracked:"// 产品数据缓存键
func ProductDataKey(productID string) string {
return fmt.Sprintf("%s%s", ProductDataPrefix, productID)
}
// 价格缓存键
func PriceCacheKey(productID string) string {
return fmt.Sprintf("%s%s", PriceCachePrefix, productID)
}
// 排名缓存键
func RankingCacheKey(productID string) string {
return fmt.Sprintf("%s%s", RankingCachePrefix, productID)
}缓存对象: 完整的产品追踪信息
缓存Key: amazon_pilot:product_data:{productID}
TTL: 30分钟
触发场景:
- GetTrackedProducts API调用
- 数据库查询后自动缓存
数据结构:
{
"id": "tracked_product_id",
"product_id": "product_uuid",
"asin": "B08N5WRWNW",
"title": "Product Title",
"brand": "Brand Name",
"current_price": 29.99,
"currency": "USD",
"bsr": 12345,
"rating": 4.5,
"review_count": 1250,
"buybox_price": 29.99,
"status": "active",
"alias": "My Product",
"images": ["url1", "url2"],
"description": "Product description",
"bullet_points": ["point1", "point2"]
}优势:
- 多用户追踪同一产品时共享缓存
- 减少数据库查询压力
- 提高API响应速度
缓存Key: amazon_pilot:price:{productID}
TTL: 1小时
用途: 缓存最新价格信息
缓存Key: amazon_pilot:ranking:{productID}
TTL: 1小时
用途: 缓存最新BSR排名信息
- TTL到期: 根据不同数据类型的TTL自动过期
- 数据更新: 产品数据更新时主动清理缓存
- 追踪状态变更: 添加/停止追踪时清理相关缓存
// 产品数据更新后清理缓存
func invalidateProductCache(ctx context.Context, productID string) {
// 清理产品数据缓存
productDataKey := cache.ProductDataKey(productID)
redisClient.Del(ctx, productDataKey)
// 清理价格缓存
priceCacheKey := cache.PriceCacheKey(productID)
redisClient.Del(ctx, priceCacheKey)
// 清理排名缓存
rankingCacheKey := cache.RankingCacheKey(productID)
redisClient.Del(ctx, rankingCacheKey)
}| 服务/场景 | 清理操作 | 清理范围 |
|---|---|---|
| AddProductTracking | 产品添加到追踪 | 清理产品数据缓存 |
| StopProductTracking | 停止追踪产品 | 清理产品数据缓存 |
| RefreshProductData | 手动刷新数据 | 清理所有产品相关缓存 |
| ApifyWorker | 数据爬取完成 | 清理所有产品相关缓存 |
- 缓存命中率: 通过业务日志统计
- 响应时间: API响应时间对比(缓存命中vs未命中)
- 内存使用: Redis内存使用情况
- 键空间: 不同类型缓存键的分布
// 记录缓存命中日志
if cacheHit {
l.Infof("Cache hit for product %s", productID)
} else {
l.Infof("Cache miss for product %s, querying database", productID)
}
// 记录缓存操作日志
l.Infof("Cached product data for product %s, TTL: 30 minutes", productID)- 热门产品数据预加载
- 用户常访问的产品优先缓存
- Pipeline操作减少网络往返
- 批量清理相关缓存
- 合理设置TTL避免内存泄漏
- 定期清理过期键
- 使用统一的Key构建函数,避免硬编码
- 数据更新后及时清理缓存,保证数据一致性
- 合理设置TTL,平衡性能和数据新鲜度
- 记录缓存操作日志,便于问题排查
- 按业务逻辑分层缓存,不同类型数据采用不同策略
直接在代码中拼接缓存键缓存用户级别的聚合数据(改为按产品缓存)忘记在数据更新时清理缓存设置过长的TTL导致数据不一致缓存大量低频访问数据
- 从按用户缓存改为按产品缓存
- 统一缓存Key管理
- 优化多用户场景下的缓存命中率
- 基于用户维度的缓存策略
- 基础的Redis缓存实现