ES索引优化教程:从分片设置到查询提速的实操步骤

先判断你的索引到底卡在哪

ES 索引优化不是把所有参数都调一遍,而是先定位瓶颈。
常见现象有三类:写入慢、查询慢、磁盘占用高。
对应的排查方向完全不同。
写入慢通常和分片数、刷新间隔、副本数有关;
查询慢要看 Mapping 字段类型、是否用到了 filter、有没有深分页;
磁盘占用高则多半是段文件太多,需要合并。

在动手前,先拿到当前索引状态。
下面命令在 Kibana Dev Tools 或 curl 里都能执行:

GET _cat/indices?v&h=index,health,pri,rep,docs.count,store.size
GET my_index/_settings
GET my_index/_mapping

重点看 pri(主分片数)、rep(副本数)、store.size(占用空间)。
如果单分片超过 50GB,或者分片总数过多导致集群 cluster.routing.allocation 频繁触发,就要考虑重建索引。

注意:索引一旦创建,主分片数无法直接修改,只能通过 _reindex 重建。副本数可以随时调整。

分片和副本怎么设才合理

分片是 ES 把数据切分存储的最小单位,分片越多并行度越高,但元数据和协调开销也越大。
零基础用户记住一个经验值:单个分片控制在 10GB 到 50GB 之间,分片总数尽量不超过节点数的 20 倍。

假设你有 3 个数据节点,每天产生 30GB 日志,保留 7 天,总数据约 210GB。
可以按每天一个索引,每个索引设 1 个主分片、1 个副本,这样每天 1 主 1 副,总主分片数 7 个,分布到 3 个节点比较均衡。

创建索引时显式指定:

PUT /logs-2025-01-01
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 1,
    "refresh_interval": "30s"
  }
}

如果已经建好索引想改副本数:

PUT /logs-2025-01-01/_settings
{
  "number_of_replicas": 0
}

把副本设为 0 可以临时加快写入,但会失去高可用,生产环境不建议长期关闭。
写入高峰期过后再调回 1。

Mapping 和刷新间隔是查询提速的关键

Mapping 决定字段怎么被索引。
默认情况下 ES 会对字符串做全文分词,如果你只是用来精确匹配的订单号、用户 ID,应该用 keyword 类型,避免不必要的分词和倒排索引膨胀。

一个常见的优化写法:

PUT /orders
{
  "mappings": {
    "properties": {
      "order_id": { "type": "keyword" },
      "status":   { "type": "keyword" },
      "amount":   { "type": "scaled_float", "scaling_factor": 100 },
      "created_at": { "type": "date" },
      "remark":   { "type": "text" }
    }
  }
}

scaled_float 比 double 更省空间,适合金额类字段。keyword 字段不会分词,做 term 查询和聚合更快。

刷新间隔 refresh_interval 控制数据写入后多久能被搜索到。
默认 1 秒,意味着每秒生成一个新段,写入压力大。
对日志类、实时性要求不高的索引,可以调成 30s 甚至 60s:

PUT /logs-2025-01-01/_settings
{
  "refresh_interval": "30s"
}

写入完成后如果要立即搜索,手动执行一次刷新:

POST /logs-2025-01-01/_refresh

段合并与查询语句检查

ES 每次刷新都会产生一个段文件,段越多查询越慢。force merge 可以把多个段合并成更少的大段,但会消耗大量 IO,建议在低峰期对只读索引执行:

POST /logs-2025-01-01/_forcemerge?max_num_segments=1

只读的历史索引合并收益最大,正在写入的索引不要频繁合并。

查询侧同样有优化空间。
能用 filter 就不用 must,因为 filter 不计算相关性得分,还能利用缓存:

GET /orders/_search
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "status": "paid" } },
        { "range": { "created_at": { "gte": "2025-01-01" } } }
      ]
    }
  }
}

避免 from + size 超过 10000 的深分页,改用 search_after。
查询时只取需要的字段,加 _source 过滤:

GET /orders/_search
{
  "_source": ["order_id", "amount"],
  "query": { "term": { "status": "paid" } }
}

改完怎么验证有没有效果

优化不是改完就算,要用数据对比。
执行下面命令观察查询耗时:

GET /orders/_search
{
  "profile": true,
  "query": { "term": { "status": "paid" } }
}

profile 会输出每个阶段的耗时,能看出是查询重写、倒排索引扫描还是取回字段慢。
也可以对比优化前后的 took 字段。

集群层面用 _cat/indices 看段数量和存储大小变化:

GET _cat/indices/orders?v&h=index,docs.count,store.size,pri,rep

如果合并后 store.size 明显下降,说明段文件被有效整理。
查询 took 从几百毫秒降到几十毫秒,说明 filter 和字段裁剪起了作用。

容易踩的坑

第一,不要一上来就把主分片设成几十个。
分片过多会导致集群状态更新变慢,甚至出现 cluster_block_exception。

第二,refresh_interval 改成 -1 会关闭自动刷新,数据写入后搜不到,必须手动 _refresh,容易误判为数据丢失。

第三,force merge 对正在写入的索引执行会阻塞写入,生产环境务必确认索引已转为只读。

第四,修改 Mapping 字段类型不能直接改,需要新建索引再 _reindex。
操作前先用 _reindex 的 dest 指向新索引,确认数据量一致后再切换别名。

第五,副本数设为 0 期间如果节点故障,数据可能丢失,只建议在可重建的数据上临时使用。

常见疑问

优化后需要重启 ES 吗? 大多数设置如 refresh_interval、number_of_replicas 都是动态的,不需要重启。
分片数变更需要重建索引,也不涉及重启集群。

小集群只有一台机器,副本数怎么设? 单节点集群副本无法分配,建议设 number_of_replicas: 0,否则集群健康会一直是 yellow。

日志索引每天一个还是按月一个? 数据量小可以按月,单月超过 50GB 建议按天,配合 ILM 生命周期策略自动删除旧索引。

query 和 filter 到底差在哪? query 会计算相关性得分并排序,filter 只判断是否匹配,结果可缓存。
做条件筛选优先用 filter,做全文搜索才用 query。

ES 索引优化的核心是让分片大小合理、Mapping 类型准确、刷新不过于频繁、段文件不过多。
先按本文命令检查现状,再针对瓶颈逐项调整,每次只改一个变量并记录前后指标,这样更容易判断哪一步真正有效。

分享到:
上一篇
Elasticsearch集群搭建:3节点生产环境实操教程
下一篇
ClickHouse性能优化:从慢查询定位到参数调优的完整排错流程
1
系统公告

泽御云中秋国庆双节活动上线:新购8折,拼团3.99元起

尊敬的用户:
泽御云“月满中秋·礼贺国庆”双节活动现已开启,活动时间为2026年9月23日至10月10日。 活动期间可享以下福利:
1. 常规云服务器新购使用优惠码“泽御中秋国庆同乐”,符合条件的订单享8折优惠。
2. 香港精品云服务器5人拼团低至3.99元,部分4核4G套餐3人拼团年付388元,续费同价。
3. 新用户购买年付云服务器,符合活动规则可赠送2个月使用时长。
4. 老用户续费季度赠15天,续费年度赠2个月;活动期间升级配置免收配置迁移手续费。
5. 推荐好友成功下单,符合条件的推荐人可获赠7天服务器使用时长。
6. 活动期间享宕机补偿标准翻倍、简单网站迁移协助及技术工单优先处理权益。
温馨提示:优惠码不适用于拼团套餐、活动轻量产品、年付订单及续费订单;拼团套餐为独立特价活动,不与赠时类福利叠加。赠送时长不可折现、退款或跨账户转移,具体规则以活动页面说明为准。
服务中心
客服
在线客服
24小时为您服务
咨询
联系我们
联系我们,为您的业务提供专属服务。
24/7 技术支持
如果您遇到寻求进一步的帮助,请过工单与我们进行联系。
24/7 即时支持
泽御云
售前客服
泽御云
泽御云
售后客服
泽御云
技术支持
评价
您对当前页面的整体感受是否满意?
😞
非常不满意
😕
不满意
😐
一般
🙂
满意
😊
非常满意