海量数据下检索变慢,八成不是集群扛不住,而是索引结构和查询写法从一开始就埋了雷。下面按索引、查询、集群、验证四条线拆开讲,每条都给可实现的做法。
索引结构:先定分片,再定字段
时序型数据别塞进一个大索引。按 “YYYY-MM” 建滚动索引,配合别名指向当前写入索引,查询侧无感知。分片数不是越多越好,单分片控制在 30-50GB,分片过多会让协调节点在每轮查询里付出成倍的调度开销。

字段类型直接决定存储和检索成本。频繁精确匹配的字段用 keyword,别用 text 再配 keyword 子字段,双份存储没必要。长文本加 ignore_above 截断:
"product_id": {
"type": "keyword",
"ignore_above": 256
}
数值字段按实际取值范围选 integer、short,别一律 long。取值范围够用就行,存储和 doc values 的内存占用都能降下来。
这里要纠正一个常见写法:Elasticsearch 7.0 起 mapping 里不再有 type 层级,8.x 更是彻底移除。如果你还在照抄带 _doc 的旧示例,直接会报错。正确写法是顶层直接放字段定义,如上例。

另外,_source 不是必须全量存。只检索部分字段时,在 mapping 里用 includes/excludes 裁掉大字段,比查询时用 docvalue_fields 兜底更彻底——后者只对 doc_values 开启的字段有效,text 字段拿不到。


查询写法:filter 优先,分页换思路
wildcard 查询基本等于全扫,尤其是前置通配。需要模糊匹配就上 ngram 分词器建索引,把匹配成本前移到写入阶段。
bool 查询里,不参与相关性评分、只做过滤的条件全部丢进 filter。filter 上下文走查询缓存,命中后几乎零成本:
"query": {
"bool": {
"filter": [
{"term": {"status": "active"}}
],
"must": [
{"match": {"description": "urgent"}}
]
}
}
深度分页是另一个重灾区。from/size 在 from+size 超过 index.max_result_window(默认 10000)时直接抛 “Result window is too large”。调大这个参数只是把内存压力往后推,不是解法。

正确路径是 search_after 配合排序字段。8.x 推荐再套一层 PIT,保证翻页期间索引滚动不会导致结果错乱:
POST /logs/_search
{
"size": 100,
"sort": [{"@timestamp": "desc"}, {"_id": "asc"}],
"search_after": [1714000000000, "abc123"]
}
排序字段必须唯一且稳定,通常用时间戳加 _id 兜底。每页返回最后一条的排序值作为下一页的 search_after 游标,内存占用与页深无关。

集群与生命周期:冷热分层,节点分工
数据节点上 SSD,单节点堆内存别超 32GB——超过后 JVM 压缩指针失效,对象头膨胀,GC 压力反而上升。历史数据迁到机械盘节点,用 ILM 自动滚动、收缩分片:
PUT _ilm/policy/hot_warm_policy
{
"phases": {
"hot": {
"actions": {
"rollover": {"max_size": "50GB"}
}
},
"warm": {
"actions": {
"shrink": {"number_of_shards": 1}
}
}
}
}
协调节点和数据节点分开部署,避免查询聚合把写入节点的堆打满。这是资源隔离,不是玄学。
验证手段:慢日志、profile、validate
改完别凭感觉。慢查询日志要分两段配,query 阶段和 fetch 阶段阈值不同:

index.search.slowlog.threshold.query.warn: 10s
index.search.slowlog.threshold.fetch.warn: 1s
query 阶段慢说明检索逻辑有问题,fetch 阶段慢多半是 _source 太大或字段太多。判定标准很直接:看日志里 took 值和对应语句,逐条 profile。
_validate/query?explain=true 能在执行前确认语句是否合法、是否走了缓存。Profile API 给出每个子查询的耗时占比,定位到具体 clause 再改。

某电商日志系统就是这么干的:大索引按日滚动拆分,常用过滤条件固化进 filter,_source 裁掉大字段,分类字段开 fielddata。改造后 P99 从 2300ms 降到 420ms,GC 次数少了六成多。单个手段的收益有限,叠起来才有这个量级。
优化没有终点。监控数据定期复盘,每次变更都过一遍 validate 和 profile,把适合自己业务的参数固化下来,比抄任何模板都管用。

ID:7850