Elasticsearch Mapping、查询与聚合¶
30 秒版(开场)¶
Mapping 定义字段类型:
text(分词全文)vskeyword(精确、聚合);Query DSL 组合bool(must/should/filter);Aggregation 做指标与桶统计。生产关键词:filter 不评分、nested、高基数 cardinality 慎用。
3 分钟版(精讲深度)¶
- Mapping:上线前定好;改字段类型通常需 reindex;
dynamic可关防字段爆炸。 - 查询:
match全文;term精确 keyword;range范围;bool组合;filter context 不参与相关性评分,符合条件且重复使用的过滤器可能由 Elasticsearch 自动缓存,并非所有 filter 都必然命中缓存。 - 聚合:
terms分桶、date_histogram按时间、avg/sum指标;pipeline agg 二次计算。
flowchart TB
Q[Query DSL] --> B[bool]
B --> M[must: 打分]
B --> F[filter: 可缓存不打分]
B --> S[should: 可选]
Q --> A[Aggregations]
A --> T[terms 桶]
A --> H[date_histogram]
10 分钟版(示例)¶
典型商品搜索 DSL
{
"query": {
"bool": {
"must": [{ "match": { "title": "手机" } }],
"filter": [
{ "term": { "status": "on_sale" } },
{ "range": { "price": { "gte": 1000, "lte": 5000 } } }
]
}
},
"sort": [{ "sales": "desc" }],
"from": 0,
"size": 20
}
| 字段类型 | 用途 | 注意 |
|---|---|---|
| text | 全文 | 需 analyzer,不宜聚合 |
| keyword | 过滤、排序、聚合 | 不分词 |
| nested | 数组对象独立查询 | 性能成本 |
| join | 父子文档 | 少用,优先宽表 |
深度分页
- 默认
index.max_result_window为 10,000,深分页避免大from→ 用 PIT + search_after 和稳定唯一 tiebreaker - Scroll 适合批量遍历/重建等场景;面向用户的深分页优先 PIT + search_after
生产场景¶
- 搜索推荐:function_score 加权
- 运营报表:terms + sub aggregation
- 日志:date_histogram + 错误率
排查与工具¶
_analyze看分词结果explain看评分- 慢查询:slowlog
架构取舍¶
| 方案 | 适用 |
|---|---|
| 宽表进 ES | 搜索列表页 |
| MySQL + ES 双写 | 复杂交易仍走 MySQL |
| 仅 keyword 字段 | 不需中文分词时简化 |
深挖问答¶
- text 和 keyword 能否改? → 一般需 reindex + 新 mapping。
- 中文分词? → IK 等插件;索引与搜索 analyzer 可不同。
- 聚合不准? →
size默认 10;doc_count_error_upper_bound;shard 级近似。 - Go 客户端? →
go-elasticsearch官方;注意 Bulk 与 context 超时。
反模式与事故¶
- 高基数
terms聚合缺少 size/shard_size 与内存评估 → 可能非常昂贵;可考虑 composite aggregation 分页 - 普通 keyword 上前导 wildcard 往往昂贵,并可能受
search.allow_expensive_queries限制;特定 wildcard field/索引设计另论 - nested 滥用 → 查询复杂、性能差
代码示例¶
// olivere/elastic(第三方客户端)风格示意;官方客户端为 go-elasticsearch
searchService := client.Search().Index("products").
Query(elastic.NewBoolQuery().
Must(elastic.NewMatchQuery("title", "手机")).
Filter(elastic.NewTermQuery("status", "on_sale")))