作为一名经历过多次电商大促的老兵,我深知搜索推荐系统对转化率的影响有多大。去年双十一,我们团队重构的搜索推荐系统扛住了百万级QPS,将转化率提升了47%。今天就来分享这套经过实战检验的SpringCloud+ES+Redis+Kafka架构方案。
先说说我们踩过的那些坑:
传统数据库的LIKE查询在电商场景下简直就是灾难。用户搜索"苹果15"时,系统会返回所有包含"苹果"和"15"的商品,导致水果苹果、苹果配件等无关商品混杂其中。更糟的是,核心商品可能因为关键词匹配度不高而排到后面。
解决方案:
当并发量上来后,几个关键问题会暴露:
我们曾在大促时出现过3秒以上的搜索延迟,直接导致跳出率飙升35%。最终通过以下方案将平均响应时间压到80ms内:
早期我们的推荐系统就是个"摆设"——对所有用户展示同样的热门商品。后来通过数据分析发现,这种粗放式推荐的点击率还不到2%。
现在的解决方案:
code复制[用户端]
↓ HTTP/2
[API Gateway] → [认证鉴权]
↓
[搜索服务] ←→ [ES集群]
↑ ↓
[推荐服务] ←→ [Redis集群]
↑ ↓
[数据同步服务] ←→ [Kafka]
↑
[商品/用户/订单服务]
经历过单体架构的痛苦转型后,我们选择SpringCloud作为微服务底座主要考虑:
服务治理能力:
配置中心:
分布式事务:
经验:SpringCloud Alibaba全家桶现在更成熟,建议新项目直接上Nacos+Sentinel组合
ES集群配置不是简单的安装就能发挥性能的,我们花了三个月调优:
json复制{
"mappings": {
"properties": {
"productId": {"type": "keyword"},
"title": {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"pinyin": {"type": "text", "analyzer": "pinyin"}
}
},
"price": {"type": "scaled_float", "scaling_factor": 100},
"tags": {"type": "keyword"},
"categories": {"type": "keyword"},
"sales": {"type": "integer"},
"location": {"type": "geo_point"}
}
}
}
缓存设计是保证性能的关键,我们的分层方案:
本地缓存(Caffeine)
分布式缓存(Redis)
防雪崩策略:
搜索API的核心处理流程:
java复制public SearchResult search(SearchRequest request) {
// 1. 查询缓存
String cacheKey = buildCacheKey(request);
SearchResult cached = cacheService.get(cacheKey);
if (cached != null) return cached;
// 2. 构建ES查询
BoolQueryBuilder query = buildBoolQuery(request);
SearchSourceBuilder source = new SearchSourceBuilder()
.query(query)
.from(request.getPage() * request.getSize())
.size(request.getSize())
.sort(buildSort(request));
// 3. 执行搜索
SearchResponse response = elasticsearchClient.search(
new SearchRequest("products").source(source),
RequestOptions.DEFAULT
);
// 4. 处理结果
SearchResult result = convertResponse(response);
// 5. 异步缓存
cacheService.asyncPut(cacheKey, result, 30, TimeUnit.SECONDS);
return result;
}
混合推荐算法流程:
召回阶段(多路并发):
排序阶段:
python复制# 排序模型示例
def train_model():
df = load_user_behavior_data()
features = ['price_sensitivity', 'brand_pref', 'category_pref']
target = 'click_probability'
model = xgb.XGBClassifier()
model.fit(df[features], df[target])
joblib.dump(model, 'rank_model.pkl')
商品变更的同步流程:
java复制@KafkaListener(topics = "product-change")
public void handleProductChange(ProductChangeEvent event) {
// 1. 更新ES
UpdateRequest updateRequest = new UpdateRequest("products", event.getProductId());
updateRequest.doc(jsonBuilder(event));
elasticsearchClient.update(updateRequest);
// 2. 清除缓存
cacheService.evict("product:" + event.getProductId());
// 3. 更新推荐模型特征
featureStore.updateProductFeatures(event);
}
当搜索变慢时,按这个顺序检查:
集群健康:
bash复制GET _cluster/health
GET _nodes/stats
索引状态:
bash复制GET _cat/indices?v
GET products/_stats
查询分析:
bash复制EXPLAIN
GET products/_search
{
"query": {...},
"profile": true
}
常见问题处理:
我们遇到过最棘手的缓存问题:
最终解决方案:
去年双十一我们做了这些准备:
容量评估:
降级方案:
监控告警:
系统上线后的核心指标变化:
| 指标 | 优化前 | 优化后 | 提升幅度 |
|---|---|---|---|
| 搜索响应时间 | 1200ms | 80ms | 93% |
| 搜索点击率 | 18% | 32% | 78% |
| 推荐商品转化率 | 1.2% | 2.8% | 133% |
| 系统可用性 | 99.2% | 99.99% | - |
这些优化最终带来的业务价值:
索引设计:
查询优化:
硬件配置:
Key设计原则:
过期策略:
监控重点:
Topic规划:
消费者组:
性能调优:
这套架构经过三年迭代和多次大促考验,最大的体会是:搜索推荐系统不是一蹴而就的,需要持续监控、分析和优化。现在我们的AB测试平台每天要运行上百个实验,这才是保持竞争力的核心。