数十亿数据量下,Elasticsearch 如何提升查询效率?¶
一、索引设计¶
1. 合理分片¶
- 单分片大小建议 10~50GB。
- 分片数一开始就规划好,索引创建后不能改 number_of_shards(可以改 number_of_replicas)。
- 数十亿数据按时间分索引(如
order-2024-01、order-2024-02),查询时只搜相关索引。
2. 路由(routing)¶
按业务字段路由,让同一类数据落到同一分片,查询时只扫一个分片:
查询也指定 routing,避免广播到所有分片。
3. 冷热分离¶
- 热数据(最近 7 天)放 SSD。
- 冷数据放 HDD。
- 老数据定期 shrink / force merge。
二、Mapping 优化¶
1. 不需要的字段不建索引¶
2. 字段类型选择¶
- 精确值用
keyword,不要用text。 - 数字用
integer/long,不要用text。 text才需要分词。
3. 避免过度分词¶
ik_max_word vs ik_smart:索引时用最大粒度,查询时用 smart。
三、查询优化¶
1. 避免深分页¶
// ❌ 深度分页,coordinate 节点要收集 from+size 条
{ "from": 10000, "size": 20 }
// ✅ search_after
{ "size": 20, "search_after": [10000] }
// ✅ scroll(导出用,不适合实时)
2. 避免 wildcard 前缀查询¶
*xxx 或 xxx* 性能差,用 match_phrase 或 ngram。
3. filter 不打分¶
filter 不计算相关性,且可缓存。
4. 只取需要的字段¶
四、写入优化¶
- 批量 bulk 写入(5~15MB 一批)。
- 增大 refresh_interval(如 30s),写入时不实时刷新。
- 写入时副本数设为 0,写完再改回来。
- 用自动生成 ID,避免版本检查。
五、集群层面¶
- 避免集群健康状态 yellow 堆积。
- JVM 堆不超过 31GB(压缩指针)。
- 磁盘水位线:85% 只读,90% 拒绝写入。
- 监控慢查询。
六、架构层面¶
- ES 是查询引擎,不是 OLTP。复杂聚合搬到 ClickHouse / Doris。
- 用 Binlog 同步 MySQL 到 ES,业务写 MySQL,查询走 ES。
面试加分
- ES 快的核心是倒排索引,不是 B+ 树。
- 深分页问题:coordinate 节点要从每个分片取 from+size 条再合并,代价 O(n*m)。