ARCHITECTURE_V2.md
7.62 KB
Suggestion 架构方案 V2(仅 Suggest,去除结果直达)
0. 结论
本方案将 Suggest 设计为独立高性能检索系统,只返回建议词,不再返回商品卡片,也不做历史兼容。
- 只保留
/search/suggestions的词级自动补全能力 - 完全移除
with_results/result_size/products[]链路 - 多语言优先,支持高并发、低延迟、可持续演进
1. 当前实现的关键问题(基于现有代码审视)
- 在线链路曾包含“suggest -> 二次商品查询”,属于典型 N+1 放大,QPS 上升后延迟和 ES 负载都不稳定。
builder.py全量构建使用“大量 in-memory 聚合 + fetchall”,大租户下内存风险高。- 查询参数上限过大(原
size<=200),不符合自动补全接口性能边界。 - 文档与实现长期混合(README 仍包含结果直达),导致认知不一致。
- 多语言归一化仍偏基础(仅 lower/空白折叠),对 Unicode、变音符、跨语系兼容不够。
2. 目标与 SLO
2.1 业务目标
- 输入时实时返回高相关建议词(query suggestion)
- 多语言稳定(至少覆盖租户配置
index_languages) - 支持词级排序和运营治理(黑白名单、降噪、降权)
2.2 性能目标(建议)
- P50
- 单集群支持高并发(千级 QPS 可横向扩展)
- 数据新鲜度:增量 5-15 分钟可见
3. 总体架构
3.1 在线路径(单跳)
Client -> API /search/suggestions -> ES search_suggestions_v2 -> 返回 suggestions
原则:
- 单次 ES 查询完成主路径(可选双召回融合,但仍在同一次 API 请求内完成)
- 不调用
search_products,不返回商品结果 - 通过
routing=tenant_id避免跨分片 fan-out
3.2 离线路径(构建)
数据源:
- 商品字段:
title.{lang}、qanchors.{lang} - 搜索日志:
shoplazza_search_log(含language/request_params) - 行为信号(可选增强):点击、加购、下单
产物:
- Suggest 文档(
tenant_id + lang + text_norm唯一) - completion + prefix 检索字段
- 排序特征(热度、近期度、质量分)
发布方式:
- 写入新物理索引(版本化)
- 原子切换 alias(零停机)
4. 索引设计(ES)
4.1 索引组织
推荐两级策略:
- 默认:环境级共享索引(降低海量租户 index 数量)
- 大租户:可升级为租户独享索引(隔离资源)
统一通过 alias 暴露:
search_suggestions_v2_current
4.2 Mapping(核心字段)
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"refresh_interval": "30s"
},
"mappings": {
"properties": {
"tenant_id": { "type": "keyword" },
"lang": { "type": "keyword" },
"text": { "type": "keyword" },
"text_norm": { "type": "keyword" },
"status": { "type": "byte" },
"sources": { "type": "keyword" },
"query_count_7d": { "type": "integer" },
"query_count_30d": { "type": "integer" },
"ctr_30d": { "type": "float" },
"order_rate_30d": { "type": "float" },
"rank_score": { "type": "float" },
"suggest": {
"type": "completion",
"contexts": [
{ "name": "tenant", "type": "category" },
{ "name": "lang", "type": "category" }
]
},
"sat": {
"properties": {
"zh": { "type": "search_as_you_type", "analyzer": "index_ik" },
"en": { "type": "search_as_you_type", "analyzer": "english" },
"ar": { "type": "search_as_you_type", "analyzer": "arabic" }
}
},
"updated_at": { "type": "date" }
}
}
}
说明:
completion负责极速前缀命中(主召回)search_as_you_type负责多词前缀和召回兜底contexts强制租户与语言隔离
5. 多语言策略
- 语言归属优先级:
log.language > request_params.language > 脚本识别 > tenant.primary_language - 统一归一化:NFKC、大小写折叠、空白折叠、标点清洗
- 分词器按语言配置:
- 中文:IK/ANSJ(与主索引保持一致)
- 拉丁语系:对应内置 analyzer
- 未覆盖语种:
standard + ICU folding兜底
- 保证写入语言必须在租户
index_languages内
6. 在线检索策略(高性能)
6.1 双通道召回(推荐)
- 通道 A:
completion suggester(prefix,skip_duplicates) - 通道 B:
multi_match(type=bool_prefix)onsearch_as_you_type - 融合去重:按
text_norm去重,按最终分排序截断
6.2 查询约束
- 默认
size=10,最大size=50 track_total_hits=false_source仅返回必要字段(text/lang/rank_score/sources)routing=tenant_id
6.3 打分建议
final_score =
es_score
+ a1*log1p(query_count_30d)
+ a2*log1p(query_count_7d)
+ a3*ctr_30d
+ a4*order_rate_30d
+ a5*freshness_decay
7. 构建与发布
7.1 构建模式
- 每日全量:重建全量特征,清理脏词
- 小时级增量:只处理新日志窗口
7.2 工程要求
- 禁止
fetchall全量入内存,改为流式读取(分页/游标) - ES 扫描采用
search_after流式聚合 - 批量写入采用 bulk(分块 + 重试 + 失败重放)
7.3 发布策略
search_suggestions_v2_YYYYMMDDHHmm写入完成- 校验 count/抽样查询/核心词覆盖
- alias 原子切换到新索引
- 保留上一个版本用于快速回滚
8. API 契约(V2)
请求:
GET /search/suggestions- 参数:
q、language、size - Header:
X-Tenant-ID
响应:
{
"query": "iph",
"language": "en",
"resolved_language": "en",
"suggestions": [
{
"text": "iphone 15",
"lang": "en",
"score": 8.31,
"rank_score": 6.72,
"sources": ["query_log", "qanchor"]
}
],
"took_ms": 12
}
删除项(明确不支持):
with_resultsresult_sizeproducts[]
9. 观测与治理
核心监控:
- QPS、P50/P95/P99、错误率
- 空结果率(按语言、按租户)
- suggestion 覆盖率(top query 是否命中)
- 语言冲突率(log vs request_params)
- 噪声词比例、黑名单命中率
治理机制:
- 黑名单:强制下线
- 白名单:强制保留并可加权
- 最小热度阈值:低频垃圾词过滤
- 时间衰减:过期词自动下沉
10. 与官方最佳实践对齐(ES)
本方案直接采用以下官方建议:
completion适合高性能自动补全,支持skip_duplicates与上下文过滤。search_as_you_type + bool_prefix是官方推荐的 as-you-type 查询方式。edge_ngram仅用于索引时分词,查询时应用普通 analyzer(search_analyzer)。- 多语言场景使用 ICU Analysis 插件增强 Unicode 处理。
- 通过
routing将租户请求路由到单分片,降低 fan-out。
11. 分阶段落地
- Phase 1(本次):去除结果直达,稳定 Suggest 单能力
- Phase 2:流式增量构建 + alias 原子发布
- Phase 3:行为信号排序(CTR/CVR)+ 运营治理台
- Phase 4:大租户独享索引自动升降级
12. Phase 2 落地命令(当前仓库)
全量重建(版本化索引 + alias 发布):
python main.py build-suggestions \
--tenant-id 162 \
--mode full \
--days 365 \
--publish-alias \
--keep-versions 2
增量更新(基于 watermark):
python main.py build-suggestions \
--tenant-id 162 \
--mode incremental \
--overlap-minutes 30
一键脚本(全量 + 增量 + ES/API 验证):
./scripts/rebuild_suggestions.sh 162