基于商品属性有序获取数据库商品的最优方案及Elasticsearch落地问询
基于Elasticsearch的电商搜索混合排序实现方案
核心实现思路
既然你已经用Elasticsearch做搜索服务,不要把排序逻辑放到数据库层实现,ES本身就是为海量数据检索、排序场景设计的,把排序计算全放在ES层完成,返回排序后的商品ID列表后再去数据库补查商品详情即可,完全避免数据库排序的性能瓶颈。
步骤1:ES索引结构设计
提前把商品核心属性、热门分同步到ES索引,不需要在查询时实时查库计算,索引参考mapping如下:
{ "mappings": { "properties": { "goods_id": {"type": "keyword"}, "goods_name": {"type": "text", "analyzer": "ik_max_word"}, "category": {"type": "keyword"}, "tags": {"type": "keyword"}, // 商品属性标签,例:"数码","高性价比","新品" "hot_score": {"type": "float"}, // 提前计算的热门分 "price": {"type": "float"}, "stock": {"type": "integer"} } } }
其中hot_score可按业务规则离线计算,比如7天销量*0.6 + 7天点击量*0.3 + 7天加购量*0.1,归一化到0~10的区间,每小时同步一次到ES即可,不需要实时更新。
步骤2:用户偏好分实时计算
用户搜索时,从用户行为模块取该用户的历史标签权重,不需要存在ES中。例:用户过去30天点击过12次数码类商品、5次家居类商品,那用户偏好权重为{"数码":0.7, "家居":0.25, "其他":0.05}。
如果是无行为数据的冷启动用户,直接把偏好权重设为0,默认按热门分排序即可。
步骤3:ES混合排序查询实现
用ES的function_score查询实现双规则加权排序,参考查询语句如下:
{ "query": { "function_score": { "query": { "match": { "goods_name": "用户输入的搜索关键词" } }, "functions": [ // 热门分权重 { "field_value_factor": { "field": "hot_score", "factor": 0.4, // 热门分的全局权重占比,可根据业务调整 "missing": 1 } }, // 用户偏好分权重 { "script_score": { "script": { "source": """ double user_score = 0; for (tag in doc['tags']) { if (params.user_tags.containsKey(tag)) { user_score += params.user_tags[tag]; } } return user_score * 0.6; // 用户偏好的全局权重占比 """, "params": { "user_tags": {"数码":0.7, "家居":0.25} // 实时传入当前用户的偏好权重 } } } } ], "boost_mode": "sum", "score_mode": "sum" } }, "size": 20, "sort": ["_score"] }
ES直接返回按最终得分排序的商品ID列表,你拿着这个ID列表去数据库查询商品详情(比如详情图、参数等不需要索引的字段)即可,不需要二次排序。
可选优化点
- 数据量较大的场景可提前给用户分人群包,同人群的偏好权重提前离线计算,减少实时计算开销
- 库存不足的商品可新增降权逻辑,在
function_score中加规则:库存小于10的商品得分打5折,避免无货商品排在前列 - 商品详情可缓存到Redis中,进一步降低数据库查询压力
避坑提醒:如果尝试在数据库层做排序,商品量级超过1万时就会出现明显的性能瓶颈,完全无法支撑电商场景的高并发访问需求。
内容的提问来源于stack exchange,提问作者Pradeep Jangid
相关产品推荐
相关产品推荐

