mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
1642 words
4 minutes
Elasticsearch 从 0 到 1(六):性能优化与生产环境注意事项
2025-06-28

Elasticsearch 从 0 到 1(六):性能优化与生产环境注意事项#

能跑通 ES 搜索接口只是第一步。

真正上线后,还会遇到这些问题:

  • 写入慢;
  • 查询慢;
  • 深分页慢;
  • 集群 yellow;
  • 磁盘占用高;
  • Mapping 设计不合理;
  • 分片太多或太少;
  • 搜索结果不稳定;
  • MySQL 和 ES 延迟同步。

这篇整理 ES 入门阶段最值得先掌握的性能和生产注意事项。

前文可以先看:

分片不是越多越好#

分片可以让数据分散到不同节点上,但分片也有管理成本。

分片太少:

  • 单个分片太大;
  • 查询和写入扩展能力有限;
  • 数据分布不够灵活。

分片太多:

  • 集群元数据变大;
  • 查询需要合并更多分片结果;
  • 内存和文件句柄消耗增加;
  • 管理成本上升。

开发环境可以简单一点,生产环境要结合数据量、节点数、增长速度来设计。

初学阶段先记住:

分片数量要提前规划,不要为了“看起来能扩展”随便开很多。

副本的作用#

副本主要有两个作用:

  • 提高可用性;
  • 分担读请求。

如果只有一个节点,却设置了 1 个副本,副本没地方放,集群健康状态可能是 yellow。

开发环境单节点可以设置:

{
"number_of_replicas": 0
}

生产环境通常至少 1 个副本。

批量写入用 Bulk#

大量写入时不要一条一条 index。

应该使用 Bulk API。

BulkRequest.Builder br = new BulkRequest.Builder();
for (ProductDocument doc : documents) {
br.operations(op -> op.index(idx -> idx
.index("products")
.id(String.valueOf(doc.getProductId()))
.document(doc)
));
}
BulkResponse response = client.bulk(br.build());

批量大小不要无限大。

可以从几百到几千条试起,结合文档大小、网络、ES 压力来调。

写完要检查失败项,不要只看请求成功。

refresh 不要乱调#

ES 写入后不是立刻就能被搜索到。

它有 refresh 机制,默认会周期性刷新,让新写入的数据变得可搜索。

如果每次写入都强制 refresh:

POST /products/_refresh

会增加系统压力。

一般搜索场景可以接受短暂延迟,不要为了“立刻搜到”频繁手动 refresh。

如果是测试环境,需要马上验证,可以临时 refresh。生产环境要谨慎。

深分页问题#

普通分页:

{
"from": 0,
"size": 10
}

浅分页没问题。

但深分页很危险:

{
"from": 100000,
"size": 10
}

ES 需要在各个分片上取出大量数据,再合并排序,开销很大。

解决思路:

  • 限制最大页数;
  • 使用 search_after
  • 使用滚动或 PIT 处理大批量遍历;
  • 业务上避免用户跳到特别深的页。

商品搜索页一般不应该让用户无限翻到几万页。

排序字段要设计好#

排序字段最好是 keyword、数值、日期这类适合排序的字段。

不要随便对 text 字段排序。

比如:

"sort": [
{ "price": "asc" },
{ "sales": "desc" }
]

如果要按标题精确排序,可以用 title.keyword,前提是 Mapping 里设计了这个子字段。

filter 比 must 更适合筛选#

商品搜索里,关键词放 must,筛选条件放 filter

{
"bool": {
"must": [
{ "match": { "title": "蓝牙耳机" } }
],
"filter": [
{ "term": { "brand": "SoundGo" } },
{ "range": { "price": { "gte": 100, "lte": 300 } } }
]
}
}

filter 不参与相关性评分,更适合品牌、分类、价格、状态这类筛选。

控制返回字段#

不要每次都返回完整文档。

可以用 _source 控制返回字段:

{
"_source": ["productId", "title", "price", "brand"],
"query": {
"match": {
"title": "蓝牙耳机"
}
}
}

这样可以减少网络传输和反序列化成本。

尤其文档里有大字段时,收益明显。

慢查询要记录#

搜索接口上线后,要记录慢查询。

建议日志里带上:

  • keyword;
  • filters;
  • sort;
  • pageNo;
  • pageSize;
  • took;
  • total;
  • requestId。

示例:

log.info("es search finished, keyword={}, pageNo={}, pageSize={}, took={}",
keyword, pageNo, pageSize, response.took());

慢查询不是只看 ES,也要看业务代码是否做了额外转换、远程调用或循环查询。

Mapping 设计影响性能#

Mapping 设计不好,后面很难优化。

常见问题:

  • 不需要搜索的字段也建了索引;
  • textkeyword 用反;
  • 大字段参与搜索;
  • 聚合字段被建成 text
  • 对象数组没用 nested
  • 字段过多导致 Mapping 膨胀。

如果字段只是展示,不需要搜索、过滤、排序,可以考虑关闭索引:

{
"description": {
"type": "text",
"index": false
}
}

当然,关闭索引后就不能查询这个字段。

JVM 和内存#

ES 是 Java 写的,对 JVM 和内存比较敏感。

生产环境要关注:

  • JVM heap;
  • GC;
  • 文件系统缓存;
  • 磁盘 IO;
  • CPU;
  • 网络;
  • 查询线程池和写入线程池。

不要把所有内存都给 JVM heap,ES 还需要文件系统缓存。

具体参数要结合官方建议和实际机器调整,不要照抄固定值。

集群监控#

至少要关注:

  • 集群健康状态;
  • 节点存活;
  • 磁盘使用率;
  • JVM heap 使用率;
  • 查询耗时;
  • 写入耗时;
  • refresh / merge 压力;
  • rejected 任务数;
  • 分片状态。

如果线程池 rejected 增加,说明 ES 已经开始拒绝任务,业务侧要及时告警。

生产环境常见建议#

整理一下常见建议:

  • 索引和 Mapping 先设计再写入;
  • 批量写入用 Bulk;
  • 不要频繁手动 refresh;
  • 限制深分页;
  • 筛选条件尽量用 filter;
  • 控制返回字段;
  • 建立慢查询日志;
  • 监控集群状态和线程池拒绝数;
  • 不把 ES 当核心交易数据库;
  • 同步失败要有补偿机制。

这一篇先记住什么#

ES 性能优化不是只调一个参数,而是索引设计、查询方式、写入方式、集群资源一起看。

入门阶段先抓住:

  • 分片不是越多越好;
  • 副本提高可用性和读能力;
  • 批量写入用 Bulk;
  • 深分页要限制;
  • 查询字段和排序字段要提前设计;
  • filter 适合筛选;
  • 搜索接口要记录慢查询;
  • 生产环境必须监控集群状态。

下一篇用电商搜索把前面内容串起来:

Elasticsearch 从 0 到 1(七):电商搜索架构实践

参考#

Share

If this article helped you, please share it with others!

Elasticsearch 从 0 到 1(六):性能优化与生产环境注意事项
https://mizuki.mysqil.com/posts/elasticsearch-06-performance-production/
Author
梦幻晨风
Published at
2025-06-28
License
CC BY-NC-SA 4.0

Some information may be outdated

Table of Contents