通用搜索方案设计
# 29.通用搜索方案设计
本篇定位:搜索是产品的"门面"——找不到内容的产品没人用。本文从一次"用户搜索"红色连衣裙"出来一堆鞋子"的故事讲起,回答三个核心问题——搜索看似简单,技术栈到底有多深?业界主流搜索架构怎么搭?怎么从 0 到 1 设计一套能扛得住的搜索系统?
# 目录介绍
# 01.搜不到的尴尬
# 1.1 红色连衣裙搜出鞋子
某电商上线新搜索引擎一周后,运营找到搜索团队拍桌子:
- 用户搜 "红色连衣裙" → 第一页一半是红色高跟鞋
- 用户搜 "iphone15" → 没有结果(库里有 "iPhone 15")
- 用户搜 "皮鞋男" → 结果都是女鞋(顺序敏感)
- 用户搜 "篮球鞋耐克" → 第一页全是阿迪、李宁
flowchart TD
A[搜索质量差] --> B[转化率掉 20%]
A --> C[搜索后跳出率高]
A --> D[用户改用淘宝/京东]
Cause[根因] --> R1[只做了简单关键词匹配]
Cause --> R2[没有同义词/纠错]
Cause --> R3[没有意图识别]
Cause --> R4[没有点击反馈优化]
style D fill:#ffebee
style Cause fill:#fff3e0
2
3
4
5
6
7
8
9
10
11
12
用搜索引擎的本质:用户输入"我想要什么",期望"立刻看到对的"——搜不到对的,等于没有搜索。
# 1.2 搜索差的扩散链路
flowchart LR
Bad[搜索差] --> User[用户搜不到想要的]
User --> A[改输关键词 N 次]
A --> B[失去耐心 → 跳出]
B --> C[去对手平台]
C --> D[流失到竞品]
Effect1[业务影响] --> E1[GMV 下降]
Effect2[ ] --> E2[用户黏性下降]
Effect3[ ] --> E3[竞争力下降]
style D fill:#ffebee
2
3
4
5
6
7
8
9
10
11
12
# 1.3 反思搜索设计
事后这个团队总结了三个最深刻的教训:
- 关键词匹配只是 1.0——现代搜索是 "理解 + 召回 + 排序 + 反馈" 的复杂系统
- 必须有同义词、纠错、意图识别——用户不会"标准输入"
- 搜索效果要靠数据闭环驱动——点击反馈是优化的金矿
搜索做不好,是产品最大的"内伤"——用户不投诉,但默默流失。
# 02.要解决的核心矛盾
# 2.1 召回与精度
graph LR
A[召回率<br/>能不能搜到] --> B[宽松匹配]
B --> C[相关结果不漏]
A2[精度<br/>排得对不对] --> B2[严格排序]
B2 --> C2[最相关在前]
Bal[平衡: 先广召回 再精排]
style Bal fill:#e8f5e8
2
3
4
5
6
7
8
9
10
业界经验:召回 1000 个候选 → 精排取前 30 个 → 展示。
# 2.2 实时与规模
| 维度 | 挑战 |
|---|---|
| 百万商品 | 索引快、内存可控 |
| 每天千万 Query | QPS 数千-数万 |
| 新商品立即可搜 | 实时索引 |
| 修改即可见 | 增量更新 |
| 容错 | 单机故障不影响全局 |
# 2.3 个性化与公平
搜索的伦理问题:
mindmap
root((个性化 vs 公平))
个性化
推送你可能买的
转化率高
千人千面
公平
新店主有曝光机会
不被算法歧视
多样性
监管
防大数据杀熟
防垄断
用户知情权
2
3
4
5
6
7
8
9
10
11
12
13
14
# 2.4 搜索的本质
搜索 = "把用户脑中的意图" 翻译成 "你库里的对应内容"
它是 NLP + 信息检索 + 排序学习 + 大数据 的综合工程。
# 03.业界主流方案
# 03.1 主流搜索引擎
| 引擎 | 特点 | 典型场景 |
|---|---|---|
| Elasticsearch | 倒排索引、全文检索、生态丰富 | 通用搜索、日志 |
| Solr | 老牌全文搜索 | 内部系统 |
| OpenSearch | ES 分叉,AWS 主推 | 云上 |
| Vespa | Yahoo 开源、强大排序 | 推荐系统 |
| Milvus / Faiss | 向量检索 | 语义搜索 |
| MySQL FullText | 数据库自带 | 小数据量 |
# 03.2 横向对比矩阵
| 维度 | MySQL LIKE | ES | Vespa | 向量库 |
|---|---|---|---|---|
| 数据量 | 10w | 亿级 | 亿级 | 亿级 |
| 召回方式 | 字符匹配 | 倒排索引 | 倒排+排序 | 向量相似 |
| 语义理解 | ❌ | 有限 | 强 | 强 |
| 复杂排序 | ❌ | 中 | 强 | 弱 |
| 实时性 | 实时 | 准实时(1s) | 准实时 | 准实时 |
| 学习成本 | 低 | 中 | 高 | 中 |
# 03.3 搜索全链路
graph LR
Q[用户 Query] --> Pre[Query 预处理]
Pre --> Und[Query 理解<br/>分词/纠错/意图]
Und --> Rec[多路召回]
Rec --> Filter[粗排过滤]
Filter --> Rank[精排]
Rank --> Mix[结果融合<br/>多样性]
Mix --> Show[展示]
Show --> Log[行为日志]
Log --> Train[训练优化]
Train -.-> Rank
style Und fill:#fff3e0
style Rec fill:#e8f5e8
style Rank fill:#e3f2fd
style Log fill:#f3e5f5
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 04.设计核心原则
# 04.1 召回优先原则
铁律:先确保"能找到",再优化"找得对"。
flowchart TD
Start[用户搜索] --> Q1{有结果?}
Q1 -->|无| Bad[最差体验<br/>用户立即跳出]
Q1 -->|有但不准| Mid[一般体验<br/>能接受]
Q1 -->|有且准| Good[最佳体验]
style Bad fill:#ffebee
style Mid fill:#fff3e0
style Good fill:#e8f5e8
2
3
4
5
6
7
8
9
10
召回兜底:模糊匹配 → 同义词 → 拼音 → 默认推荐——总要有结果。
# 04.2 多路并联原则
单一召回路径覆盖不全 → 多路并联召回:
graph LR
Q[Query] --> R1[路径 1<br/>关键词倒排]
Q --> R2[路径 2<br/>同义词扩展]
Q --> R3[路径 3<br/>语义向量]
Q --> R4[路径 4<br/>类目匹配]
Q --> R5[路径 5<br/>历史行为]
R1 & R2 & R3 & R4 & R5 --> Merge[合并去重]
Merge --> Top[Top 1000 候选]
style Merge fill:#e8f5e8
2
3
4
5
6
7
8
9
10
11
# 04.3 排序可调原则
排序公式必须可灵活调权——业务不同时期诉求不同。
# 排序公式(线性加权)
score =
0.4 × text_relevance # 文本相关
+ 0.3 × ctr_predict # 点击预估
+ 0.1 × sales_count # 销量
+ 0.1 × price_score # 价格
+ 0.1 × freshness # 新鲜度
# 大促期间临时调整
boost_promotion: 1.2 # 活动商品加权
2
3
4
5
6
7
8
9
10
# 04.4 反馈闭环原则
用户行为是最好的标注师:
graph LR
User[用户行为] --> Log[日志]
Log --> Analy[分析]
Analy --> Feature[特征]
Feature --> Model[模型训练]
Model --> Online[在线服务]
Online --> User
Note[点击/购买/收藏/复购<br/>都是排序学习的标签]
style Note fill:#fff3e0
2
3
4
5
6
7
8
9
10
11
# 05.方案落地实战
# 05.1 整体架构
graph TB
subgraph "数据源"
DB[(业务库)]
Log[(行为日志)]
end
subgraph "索引层"
Build[索引构建]
ES[ES 集群]
Vec[向量库]
DB --> Build --> ES & Vec
end
subgraph "查询层"
API[搜索 API]
Und[Query 理解]
Rec[多路召回]
Rank[精排]
Filter[过滤/打散]
end
subgraph "训练层"
Feature[特征]
Train[排序模型训练]
Log --> Feature --> Train
Train --> Rank
end
API --> Und --> Rec
Rec --> ES & Vec
Rec --> Rank --> Filter --> Result[结果]
style Und fill:#fff3e0
style Rec fill:#e8f5e8
style Rank fill:#e3f2fd
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 05.2 索引构建
典型索引字段设计(以电商为例):
{
"id": "p_12345",
"title": "Nike 男士跑步鞋 黑色 42 码",
"title_pinyin": "naike nanshi paobuxie",
"title_synonym": ["nike", "耐克", "运动鞋"],
"category_path": ["鞋靴", "男鞋", "跑步鞋"],
"brand": "Nike",
"tags": ["跑步", "黑色", "42 码"],
"price": 599,
"sales": 8000,
"ctr_30d": 0.085,
"embedding": [0.12, -0.34, ...] // 语义向量
}
2
3
4
5
6
7
8
9
10
11
12
13
索引构建流程:
sequenceDiagram
participant DB as 业务库
participant Pipe as 数据管道
participant Vec as 向量服务
participant ES as ES
DB->>Pipe: 商品变更 binlog
Pipe->>Pipe: 字段映射 + 清洗
Pipe->>Vec: 调向量服务 embed
Vec-->>Pipe: 768 维向量
Pipe->>ES: 写入索引
Note over ES: 1 秒内可搜索
2
3
4
5
6
7
8
9
10
11
12
# 05.3 Query 理解
Query 理解的几个关键步骤:
flowchart LR
Q[原始 Query<br/>iphone15 手机壳] --> Norm[归一化<br/>大小写/全半角]
Norm --> Correct[纠错<br/>iphon15 → iphone15]
Correct --> Seg[分词<br/>iphone15 / 手机壳]
Seg --> Syn[同义词<br/>苹果 = iphone]
Syn --> Intent[意图识别<br/>主体: 手机壳<br/>修饰: iPhone 15]
Intent --> Cat[类目识别<br/>手机配件]
style Intent fill:#fff3e0
2
3
4
5
6
7
8
9
意图识别的好处:
| Query | 主体 | 修饰 | 类目 |
|---|---|---|---|
| "篮球鞋耐克" | 篮球鞋 | 耐克 | 鞋靴 |
| "耐克的篮球鞋" | 篮球鞋 | 耐克 | 鞋靴 |
| "iPhone 15 手机壳" | 手机壳 | iPhone 15 | 手机配件 |
→ 这样能避免"红色连衣裙搜出红色高跟鞋"的问题。
# 05.4 多路召回
经典多路召回组合:
graph TB
Q[Query]
Q --> P1[1 文本召回<br/>BM25]
Q --> P2[2 类目召回<br/>同类目商品]
Q --> P3[3 向量召回<br/>语义相似]
Q --> P4[4 i2i 协同<br/>看了又看]
Q --> P5[5 个性化召回<br/>历史偏好]
Q --> P6[6 热销召回<br/>兜底]
P1 & P2 & P3 & P4 & P5 & P6 --> Merge[合并 + 去重<br/>1000 候选]
style Merge fill:#e8f5e8
2
3
4
5
6
7
8
9
10
11
12
13
# 05.5 排序融合
典型两阶段排序:
graph LR
Recall[1000 召回] --> Coarse[粗排<br/>简单模型 LR]
Coarse --> Top200[Top 200]
Top200 --> Fine[精排<br/>复杂模型 GBDT/DNN]
Fine --> Top30[Top 30]
Top30 --> Mix[多样性打散<br/>同品牌不连续]
Mix --> Final[展示 30]
style Coarse fill:#e3f2fd
style Fine fill:#fff3e0
style Mix fill:#e8f5e8
2
3
4
5
6
7
8
9
10
11
精排特征:
- 文本特征:BM25、字段权重
- 统计特征:点击率、转化率、销量
- 个性化特征:用户画像、历史
- 上下文特征:时间、地理、设备
# 06.关键问题解决
# 06.1 同义词与纠错
同义词词典管理:
# 品牌同义词
"苹果" ⇔ "Apple" ⇔ "iPhone"
"耐克" ⇔ "Nike" ⇔ "NIKE"
"阿迪" ⇔ "Adidas" ⇔ "ADIDAS"
# 类目同义词
"裤子" ⇔ "长裤" ⇔ "休闲裤"
"包包" ⇔ "手提包" ⇔ "皮包"
# 错别字纠错
"袷克" → "夹克"
"连衣群" → "连衣裙"
"篮求鞋" → "篮球鞋"
2
3
4
5
6
7
8
9
10
11
12
13
典型实现:
- 编辑距离纠错(Damerau-Levenshtein)
- 拼音纠错(zhongwen / zhōng wén)
- 用户行为纠错(搜 A 后改搜 B → A 是 B 的错别字)
# 06.2 长尾 Query
长尾 Query 占总量的 80%——但每个 Query 量很少。
graph LR
A[Query 分布] --> B[头部 Query 20%<br/>占 80% 流量]
A --> C[长尾 Query 80%<br/>占 20% 流量]
B --> Cache1[缓存 + 精细优化]
C --> Cache2[依赖泛化能力<br/>语义召回 + 兜底]
style B fill:#e8f5e8
style C fill:#fff3e0
2
3
4
5
6
7
8
9
长尾应对:
- 语义向量召回(不依赖关键词匹配)
- 同义词扩展
- 兜底推荐(无结果时给热销)
- Query 改写(重写为头部 Query)
# 06.3 数据实时性
| 业务 | 实时性要求 | 技术方案 |
|---|---|---|
| 新商品上架 | 1 分钟内可搜 | binlog → ES(1s 延迟) |
| 库存变化 | 5 分钟内 | 异步同步 |
| 价格变化 | 1 分钟 | 实时同步 |
| 下架商品 | 立即 | 同步删除索引 |
# 07.常见陷阱与反例
# 07.1 全文匹配反例
反例:用 MySQL LIKE '%xxx%' 实现搜索 → 数据量大后全表扫描 → 1 个查询 5 秒。
教训:
- 数据量 > 10w 必须用专业搜索引擎
- ES 是事实标准
# 07.2 重排序丢主路反例
反例:用复杂模型重排但忘了"主路兜底"——模型超时 → 整个搜索失败白屏。
教训:
- 排序必须有降级策略
- 复杂模型超时 → 降级到简单模型
- 简单模型也挂 → 直接按销量排
# 07.3 无降级反例
反例:ES 集群挂了 → 整个搜索瘫痪 → 整个 App 不可用。
教训:
- 搜索必须能降级
- 降级到数据库 LIKE(虽慢但能用)
- 降级到默认推荐
- 给用户友好提示
mindmap
root((三大反例))
全文 LIKE
数据大慢
全表扫描
用 ES
重排无主路
模型挂
整体白屏
多级兜底
无降级
ES 挂全瘫
用户失望
多级降级
2
3
4
5
6
7
8
9
10
11
12
13
14
# 08.演进路线
# 08.1 V1 数据库 LIKE
特征:业务起步、商品少。
做法:
- MySQL LIKE / FullText
- 简单分页
适用阶段:< 10w 数据 / POC
# 08.2 V2 ES 倒排索引
特征:业务规模化、需要专业搜索。
做法:
- ES 集群
- 同义词 + 纠错
- 简单排序公式
适用阶段:百万-千万级数据
# 08.3 V3 多路召回 + 排序
特征:搜索是核心入口、需要算法驱动。
做法:
- Query 深度理解
- 多路召回
- 学习排序(LTR)
- 个性化
- A/B 实验平台
适用阶段:头部电商 / 内容平台
flowchart LR
V1[V1 LIKE<br/>起步] --> V2[V2 ES<br/>主流]
V2 --> V3[V3 多路召回 + LTR<br/>头部]
style V1 fill:#e3f2fd
style V2 fill:#e8f5e8
style V3 fill:#fff3e0
2
3
4
5
6
7
# 09.总结与决策
# 09.1 上线检查表
搜索系统上线前对照:
- [ ] 索引设计(字段、分词、同义词)
- [ ] 数据实时同步管道
- [ ] Query 预处理(归一化 / 纠错 / 分词)
- [ ] 同义词词典
- [ ] 多路召回
- [ ] 排序公式
- [ ] 多样性打散
- [ ] 兜底策略(无结果时给推荐)
- [ ] 降级策略(ES 挂 → 数据库)
- [ ] 缓存(热门 Query)
- [ ] 行为日志埋点(曝光 / 点击 / 转化)
- [ ] 监控(QPS / RT / 无结果率 / CTR)
- [ ] A/B 实验框架
- [ ] 排序模型迭代流程
# 09.2 选型决策树
flowchart TD
Start([搜索方案选型]) --> Q1{数据量?}
Q1 -->|< 10w| MySQL[MySQL FullText<br/>简单够用]
Q1 -->|10w-亿级| ES[Elasticsearch<br/>主流方案]
Q1 -->|亿级 + 复杂排序| Vespa[Vespa<br/>+ 向量库]
Q2([语义需求?]) --> Sem{需要语义理解?}
Sem -->|是| Vec[+ 向量检索<br/>Milvus/Faiss]
Sem -->|否| NoVec[关键词足够]
Q3([排序需求?]) --> Rank{需要个性化?}
Rank -->|是| LTR[+ 学习排序<br/>LightGBM / DNN]
Rank -->|否| Rule[简单加权公式]
style MySQL fill:#e3f2fd
style ES fill:#e8f5e8
style Vespa fill:#fff3e0
style Vec fill:#f3e5f5
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
最后一句话:搜索好坏决定产品的"找得到性"——开篇"红色连衣裙搜出鞋子"的根因,是把搜索当成了"字符串匹配"。
好的搜索方案 = 召回全面、排序智能、实时反馈、降级兜底。