高 困难
ES性能调优#
一句话答案#
ES 调优三板斧:写入优化(bulk 批量 + refresh_interval 调大 + 关闭副本)、查询优化(filter 缓存 + routing 避免跨分片 + search_after 替代深分页)、集群优化(分片规划 + 冷热分离 + JVM 堆 ≤ 31GB)。
核心要点
写入优化#
| 手段 | 效果 | 场景 |
|---|---|---|
| Bulk 批量写入 | 减少网络开销 | 所有写入场景 |
| refresh_interval: ”30s” | 减少 segment 生成频率 | 写入量大时 |
| number_of_replicas: 0 | 写入时不同步副本 | 全量导入阶段 |
| translog.durability: async | 异步刷盘 | 允许少量丢失 |
| 合理 Mapping | 不需要搜索的字段设 index:false | 所有场景 |
// 全量导入时的临时设置
PUT /my_index/_settings
{
"refresh_interval": "-1",
"number_of_replicas": 0
}
// 导入完成后恢复
PUT /my_index/_settings
{
"refresh_interval": "1s",
"number_of_replicas": 1
}json查询优化#
1. Filter 替代 Must(不需要评分的条件)
→ filter 结果缓存为 Bitset,重复查询直接命中
2. Routing 路由(避免全分片扫描)
GET /orders/_search?routing=user_123
→ 只查询 user_123 所在的那个分片
3. 深分页优化
❌ from=10000, size=10 (每个分片返回 10010 条再合并)
✅ search_after + PIT (Point in Time)
4. 字段裁剪
"_source": ["title", "price"] // 只返回需要的字段
5. 预计算
提前聚合结果存入 summary 索引,查询时直接取plaintext深分页问题#
from + size 的问题:
from=10000, size=10
→ 每个 Shard 取 Top 10010
→ Coordinating 节点合并 5×10010=50050 条
→ 取第 10001-10010 条
→ 内存爆炸 + 响应慢
解决方案:
方案1: search_after (推荐)
{"search_after": [上一页最后一条的 sort value], "size": 10}
→ 每个 Shard 只取 10 条
方案2: Scroll API (废弃中,用 PIT 替代)
适合全量导出,不适合实时搜索plaintext集群级优化#
JVM 设置:
-Xms 和 -Xmx 设为相同(避免 resize)
堆内存 ≤ 31GB(超过则无法用 Compressed OOP,浪费内存)
建议给 OS 留一半内存(Page Cache 缓存 Lucene 文件)
分片规划:
单 Shard: 10-50GB
每 GB heap 最多 20 个 Shard
避免超大 Shard(恢复慢)和超多小 Shard(Master 压力大)
冷热分离:
热数据 → SSD 节点(node.attr.box_type: hot)
冷数据 → HDD 节点(node.attr.box_type: cold)
通过 ILM (Index Lifecycle Management) 自动迁移plaintextSegment Merge 优化#
问题:大量小 Segment 影响查询性能(每次查询需要遍历所有 Segment)
策略:
- max_num_segments: force merge 到指定数量(写入完成后执行)
- merge.scheduler.max_thread_count: 控制合并线程数
- 不在高峰期做 force merge
POST /my_index/_forcemerge?max_num_segments=1plaintext面试回答(2分钟版)
ES 调优分写入和查询两方面。写入优化:用 bulk 批量提交减少网络开销,全量导入时把 refresh_interval 设为 -1 且副本设为 0,导完再恢复,吞吐量可以提升数倍。查询优化:不需要评分的过滤条件放 filter(有 Bitset 缓存),按 routing 路由避免全分片扫描,深分页必须用 search_after 替代 from+size(否则每个分片取 from+size 条再合并,内存爆炸)。集群层面:JVM 堆不超过 31GB(Compressed OOP 边界),给 OS 留一半内存用于 Page Cache 缓存 Lucene 文件。冷热分离用 ILM 自动管理——热数据在 SSD,超过 7 天移到 HDD。Segment 合并在低峰期 force merge 减少 segment 数量提升查询效率。
追问与易错
追问方向:
- “为什么堆不能超过 31GB?”→ JVM Compressed OOP 临界点,超过后指针从 4 字节变 8 字节,等效容量反而下降
- “深分页为什么慢?”→ 每个 Shard 都要取 from+size 条做全局排序,Shard 越多越慢
- “怎么监控 ES 性能?”→ _cat/nodes, _cat/shards, _nodes/stats + Prometheus/Grafana
- “force merge 有什么风险?”→ IO 密集,高峰期做会影响查询,且不可中断
易错点:
- ❌ “堆内存越大越好”——超过 31GB 性能反而下降(失去 Compressed OOP)
- ❌ “search_after 能跳页”——只能逐页向后翻,不支持跳到第 N 页
- ❌ “refresh_interval=0 就是实时”——0 不是关闭,-1 才是关闭;0 表示每次写入都 refresh(性能极差)