概念
搜索:用户有明确意图,输入关键词,系统返回相关结果。核心是相关性。
推荐:用户没有明确意图,系统根据用户行为和偏好主动推送内容。核心是个性化。
核心原理
1. 搜索引擎架构(Elasticsearch)
集群架构
| 概念 | 说明 |
|---|---|
| Cluster | 一组节点组成的集群,共同对外提供服务 |
| Node | 单个 ES 实例,可承担 Master / Data / Coordinating 角色 |
| Index | 逻辑上的数据集合,类似关系型数据库中的表 |
| Shard | Index 的物理分片,数据水平拆分到多个节点 |
| Replica | 分片的副本,提供高可用和读扩展能力 |
倒排索引(Inverted Index)
倒排索引是搜索速度快的根本原因。建立时将文档中的词项(Term)映射到包含该词项的文档列表。
词项 → 文档列表(DocID + 位置)
"手机" → [doc1(pos:3), doc5(pos:1), doc9(pos:7)]
"华为" → [doc1(pos:1), doc3(pos:4)]
"5G" → [doc1(pos:5), doc5(pos:3)]查询时直接查词项表,取交集/并集,时间复杂度远低于全表扫描。
文档写入流程
客户端写入
↓
Coordinating Node(路由计算:shard = hash(doc_id) % primary_shards)
↓
Primary Shard(写入 In-memory buffer + Translog)
↓
refresh(默认 1s)→ 生成 Segment,文档变为可搜索状态(Near Real-Time)
↓
flush → Segment 持久化到磁盘,Translog 清空
↓
Replica Shard(同步复制)近实时搜索(NRT):refresh_interval 默认 1 秒,新写入的文档最多延迟 1 秒可被搜索到。写入压力大时可调大该值(如 30s)以提升吞吐。
2. 搜索优化
相关性评分
- TF-IDF:词频(TF)越高、逆文档频率(IDF)越高,得分越高。IDF 体现词的区分度("的"这种高频词 IDF 很低)。
- BM25(ES 默认):TF-IDF 的改进版,对高词频做饱和处理,加入文档长度归一化,结果更稳定。
多字段搜索与权重提升
{
"multi_match": {
"query": "华为手机",
"fields": ["title^3", "description^1", "brand^2"],
"type": "best_fields"
}
}^3 表示 title 字段权重是 description 的 3 倍。
分析器与分词器
| 场景 | 分析器 |
|---|---|
| 英文标准分词 | standard |
| 中文分词 | ik_max_word(细粒度)/ ik_smart(粗粒度) |
| 拼音搜索 | pinyin analyzer |
| 精确匹配 | keyword(不分词) |
自动补全(Auto-Complete):使用 completion 字段类型 + suggest API,响应速度 < 10ms。
模糊搜索(Fuzzy Search):fuzziness: "AUTO" 允许一定编辑距离,处理拼写错误。
高亮(Highlight):返回结果中标记命中词项,提升用户体验。
3. 搜索基础设施
索引设计(Mapping)
{
"mappings": {
"properties": {
"title": { "type": "text", "analyzer": "ik_max_word", "boost": 3 },
"price": { "type": "double" },
"category_id": { "type": "keyword" },
"created_at": { "type": "date" },
"tags": { "type": "keyword" }
}
}
}text:全文检索,需要分词keyword:精确匹配、聚合、排序- 字段一旦创建类型不可更改,需要 reindex
分片策略
- 单个分片建议保持在 10–50 GB
- 分片数 = 预估数据量 / 单分片目标大小
- 分片数确定后不可修改(需提前规划),副本数可动态调整
冷热数据分层(ILM)
Hot(高性能 SSD)→ Warm(普通 HDD)→ Cold(对象存储)→ Delete
写入/查询 只读/压缩 归档/低频 自动删除数据同步:MySQL → ES
MySQL(业务数据库)
↓ binlog
Canal(监听 binlog,解析变更事件)
↓
Kafka(消息队列,削峰解耦)
↓
Consumer(消费消息,调用 ES Bulk API)
↓
Elasticsearch(搜索索引)- Canal 伪装成 MySQL 从节点,实时捕获 binlog
- Kafka 保证消息不丢失,支持重放
- 消费端做幂等处理(以主键为文档 ID,upsert 操作)
4. 推荐系统架构:四阶段 Pipeline
海量候选集(千万级)
|
[召回 Recall] — 多路策略快速筛选,候选集 → 千级
|
[粗排 Pre-Rank] — 简单模型快速打分,千级 → 百级
|
[精排 Rank] — 复杂深度模型精细打分,百级 → 十级
|
[重排 Re-Rank] — 去重、多样性、业务干预,十级 → 最终结果
|
用户 Feed| 阶段 | 目标 | 典型技术 |
|---|---|---|
| 召回 | 从海量内容中快速捞出相关候选 | 协同过滤、向量检索(ANN)、倒排索引 |
| 粗排 | 快速过滤低质候选,降低精排压力 | LR、GBDT、简单 DNN |
| 精排 | 精细化排序,最大化点击/转化 | Wide & Deep、DeepFM、Transformer |
| 重排 | 业务规则、多样性、打散、广告插入 | MMR 算法、规则引擎 |
5. 召回策略
协同过滤(Collaborative Filtering)
- UserCF:找到兴趣相似的用户,推荐他们喜欢但当前用户未看过的内容
- 适合用户量小、社交场景
- ItemCF:找到与用户历史行为物品相似的物品推荐
- 适合物品量小、电商场景,结果可解释性强
基于内容(Content-Based):提取物品特征(标签、类目、作者),与用户历史偏好匹配。
热门/新品兜底:解决冷启动,对新用户推送热门内容;对新物品推送给相关用户群体测试。
多路召回(Multi-Channel Recall):
UserCF 召回 ──┐
ItemCF 召回 ──┤
向量召回 ──┼──→ 合并去重 → 粗排
热门召回 ──┤
新品召回 ──┘多路并行,各路召回互补,提高覆盖率和多样性。
6. 特征工程
| 特征类别 | 示例 |
|---|---|
| 用户特征 | 年龄、性别、城市、历史点击、兴趣标签 |
| 物品特征 | 类目、标签、发布时间、热度、质量分 |
| 上下文特征 | 当前时间、设备类型、网络环境、地理位置 |
| 实时特征 | 过去 1 小时点击率、当前会话行为序列 |
实时特征更新:用户行为(点击/滑动/停留)通过埋点上报 → Kafka → Flink 实时计算 → 写入特征存储(Redis / Feature Store)→ 排序模型实时读取。
实战案例
案例一:电商商品搜索
背景:商品数量 5000 万,支持中文全文搜索、价格/类目过滤、按销量/评分排序。
架构设计
用户搜索请求
↓
搜索服务(Query 解析、同义词扩展)
↓
Elasticsearch 集群(3 个 Data Node,共 10 个 Primary Shard)
↓
返回结果(高亮 + 聚合统计)
数据同步链路:
MySQL(商品库)→ Canal → Kafka → ES Consumer → Elasticsearch关键设计决策
- 中文分词:title 字段同时索引
ik_max_word(检索)和ik_smart(高亮),兼顾召回率和精准度 - 同义词:搜索"手机"时自动扩展"智能手机"、"移动电话"
- 价格/库存过滤:用
filter子句(不影响评分,有缓存) - 排序:支持按相关性(默认)、销量、价格、上架时间多维度排序
- 缓存:热门搜索词结果缓存到 Redis,TTL 5 分钟
案例二:短视频推荐 Feed
背景:日活用户 5000 万,视频库 10 亿,要求首屏 200ms 内返回推荐列表。
四阶段流程
1. 召回(< 50ms)
- UserCF:基于相似用户历史喜好
- ItemCF:基于用户最近看过的视频
- 向量召回:用户兴趣向量 ANN 检索(Faiss)
- 热门兜底:保证新用户也有内容
→ 合并得到 ~1000 个候选视频
2. 粗排(< 30ms)
- 用轻量 GBDT 模型预估 CTR
- 过滤用户已看过的视频、违规内容
→ 保留 ~200 个
3. 精排(< 80ms)
- DeepFM 模型预估 CTR、完播率、点赞率
- 综合评分 = α×CTR + β×完播率 + γ×点赞率
→ 保留 ~20 个
4. 重排(< 20ms)
- 同作者/同话题打散(避免连续推同类)
- 插入广告(按策略)
- 新视频强插(保证新内容曝光)
→ 最终返回 10 个实时特征:用户当前会话的最近 5 次点击行为通过 Flink 实时计算,注入精排特征。
面试常问 & 怎么答
Q1:ES 和 MySQL 全文索引的区别?
怎么答:
从三个维度展开——底层结构、功能能力、适用场景。
- 底层结构:MySQL 全文索引基于 B+ 树或倒排索引(InnoDB FTS),但实现较为简单;ES 的倒排索引更完善,天然支持分布式、分片。
- 功能能力:ES 支持中文分词(IK)、多字段搜索、相关性评分(BM25)、高亮、聚合分析、近实时搜索;MySQL 全文索引不支持中文(需插件)、功能有限。
- 扩展性:ES 横向扩展简单(加节点),MySQL 全文检索在大数据量下性能急剧下降。
- 结论:简单场景(数据量小、只需 LIKE)用 MySQL;复杂全文检索(中文、相关性、大数据量)上 ES,数据通过 Canal + Kafka 同步。
Q2:推荐系统如何解决冷启动问题?
怎么答:
分新用户冷启动和新物品冷启动分别回答。
新用户冷启动:
- 注册时收集基础信息(年龄、城市、兴趣偏好选择)
- 前期推送热门内容、平台爆款
- 根据用户前几次行为快速更新兴趣模型(探索期,多样性优先)
- 利用物品内容特征(Content-Based)做冷启动推荐
新物品冷启动:
- 初期强制给一定曝光量(流量倾斜)
- 用物品的标签/类目做基于内容的匹配,推给相关兴趣用户
- 通过 Bandit 算法(UCB、Thompson Sampling)平衡探索与利用
- 收集足够点击数据后过渡到协同过滤
Q3:倒排索引的原理?为什么搜索速度快?
怎么答:
原理:文档写入时,分析器将文本拆分为词项(Token),建立"词项 → 文档列表"的映射表,即倒排索引。每个词项对应一个 Posting List,记录包含该词项的所有文档 ID 及词频、位置信息。
为什么快:
- 查询时直接查词项表,O(1) 定位(哈希或 B 树),无需全表扫描
- 多词查询只需对 Posting List 做集合运算(AND/OR),高度优化
- Posting List 经过压缩(Frame of Reference、Roaring Bitmap),内存占用小,可缓存
对比全表扫描(LIKE '%关键词%'):全表扫描是 O(N×M),倒排索引是 O(词表查找 + Posting List 合并),数量级差异在大数据量下极为显著。
看到什么就先想到这类
| 看到这些关键词 | 先想到 |
|---|---|
| 全文搜索 / 关键词匹配 | Elasticsearch + 倒排索引 |
| 搜索相关性 / 排序不准 | BM25 / TF-IDF / 字段权重调优 |
| 中文搜索 / 拼音搜索 | IK 分词器 / Pinyin 分析器 |
| 个性化推荐 / Feed 流 | 召回 → 粗排 → 精排 → 重排 Pipeline |
| 冷启动(新用户/新内容) | 热门兜底 + 内容推荐 + Bandit 算法 |
| 实时特征 / 行为实时更新 | Flink + Kafka 实时计算 |
| 数据库数据同步到 ES | Canal + Kafka + Consumer |
| 海量候选快速过滤 | 多路召回 + 粗排 |
| 推荐多样性 / 避免信息茧房 | 重排阶段 MMR / 打散策略 |