设计搜索系统
问题
如何设计一个支持全文检索、搜索建议和高亮的搜索系统?
答案
一、整体架构
二、数据同步方案
MySQL(主数据源)
│
├── 方案 1:CDC(Canal + Kafka)
│ MySQL binlog → Canal → Kafka → ES Consumer
│ 优点:实时性好,对应用无侵入
│
├── 方案 2:双写
│ 应用同时写 MySQL 和 ES
│ 优点:简单直接
│ 缺点:一致性、代码侵入
│
└── 方案 3:定时全量
Schedule Job → MySQL 查询 → ES Bulk
优点:一致性最好
缺点:延迟高
推荐方案:CDC(实时增量)+ 定时全量(兜底校正)。
三、索引设计
商品搜索索引
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"analysis": {
"analyzer": {
"ik_smart_pinyin": {
"type": "custom",
"tokenizer": "ik_smart",
"filter": ["pinyin_filter", "lowercase"]
}
},
"filter": {
"pinyin_filter": {
"type": "pinyin",
"keep_original": true,
"keep_first_letter": true
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart",
"fields": {
"pinyin": { "type": "text", "analyzer": "ik_smart_pinyin" },
"keyword": { "type": "keyword" }
}
},
"description": { "type": "text", "analyzer": "ik_max_word" },
"category": { "type": "keyword" },
"price": { "type": "double" },
"sales": { "type": "integer" },
"created_at": { "type": "date" },
"suggest": { "type": "completion" }
}
}
}
四、搜索查询
综合搜索请求
{
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": "苹果手机",
"fields": ["title^3", "title.pinyin", "description"],
"type": "best_fields"
}
}
],
"filter": [
{ "term": { "category": "手机" } },
{ "range": { "price": { "gte": 1000, "lte": 10000 } } }
]
}
},
"sort": [
{ "_score": "desc" },
{ "sales": "desc" }
],
"highlight": {
"fields": { "title": {}, "description": {} },
"pre_tags": ["<em>"],
"post_tags": ["</em>"]
},
"aggs": {
"categories": { "terms": { "field": "category", "size": 10 } },
"price_range": {
"range": {
"field": "price",
"ranges": [
{ "to": 1000 },
{ "from": 1000, "to": 5000 },
{ "from": 5000 }
]
}
}
},
"from": 0,
"size": 20
}
五、搜索建议(自动补全)
{
"suggest": {
"title-suggest": {
"prefix": "苹果",
"completion": {
"field": "suggest",
"size": 5,
"fuzzy": { "fuzziness": "AUTO" }
}
}
}
}
六、搜索优化
| 优化项 | 说明 |
|---|---|
| 同义词扩展 | "手机" → "手机, 智能手机, 移动电话" |
| 拼写纠错 | "pingguo" → "苹果" |
| 拼音搜索 | "pingguo" 可以搜到 "苹果" |
| 停用词过滤 | 去掉"的"、"了"等无意义词 |
| 结果缓存 | 热门搜索词的结果缓存到 Redis |
| 深分页优化 | 使用 search_after 替代 from + size |
常见面试问题
Q1: ES 和 MySQL LIKE 查询的区别?
答案:
| 维度 | MySQL LIKE | Elasticsearch |
|---|---|---|
| 查询方式 | LIKE '%keyword%' 全表扫描 | 倒排索引精确命中 |
| 性能 | 百万级数据就慢 | 亿级数据毫秒响应 |
| 分词 | 不分词,精确匹配 | 分词后匹配 |
| 相关性 | 无评分 | TF-IDF / BM25 评分 |
| 高亮 | 需应用层处理 | 内置支持 |
Q2: 如何保证 ES 和 MySQL 的数据一致性?
答案:
- CDC 增量同步:Canal 监听 binlog,准实时同步
- 定时全量校验:每天凌晨对比两边数据量和抽样内容
- 补偿机制:发现不一致自动重新同步
- 容忍短暂不一致:搜索场景允许秒级延迟,MySQL 是主数据源