You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何排查Elasticsearch查询耗时超400ms的原因?

Elasticsearch查询耗时过长排查求助

我的搜索查询耗时超过400ms(通过Jaeger追踪),并发请求数约10次,速度过慢,希望帮忙排查原因。

Jaeger追踪截图

索引基本信息

  • 索引包含46000条记录,分为5个分片

Mapping配置

"table_name" : {
  "type" : "text",
  "analyzer" : "autocomplete",
  "search_analyzer" : "standard"
},
"table_name_rough" : {
  "type" : "text",
  "analyzer" : "autocomplete",
  "search_analyzer" : "standard"
},

查询代码实现

BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

BoolQueryBuilder boolQueryBuilderTableName = new BoolQueryBuilder();
boolQueryBuilder.must(QueryBuilders.termQuery("is_deleted", 0));
if (Strings.isNotNullOrEmpty(keyword)) {
    boolQueryBuilderTableName.should(QueryBuilders.matchPhraseQuery("table_name", keyword));
    boolQueryBuilderTableName.should(QueryBuilders.matchPhraseQuery("table_name_rough", keyword));

//    table_name 存储带重音的内容,比如 "hôm nay", "tìm kiếm"
//    table_name_rough 对应不带重音的内容,比如 "hom nay", "tim kiem"

boolQueryBuilderTableName.minimumShouldMatch(1);
}

if (Strings.isNotNullOrEmpty(userId)) {
    boolQueryBuilder.must(QueryBuilders.termQuery("user_id", String.valueOf(userId)));
}

boolQueryBuilder.must(boolQueryBuilderTableName);

Span spanES = OpenTracerManager.getInstance().buildSpan("query ES").start();
SearchResponse searchResponse = tableElasticRepository.searchElastichByQuery(limit, page, boolQueryBuilder);
span.finish();

SearchResponse实现代码

public SearchResponse searchElasticByQuery(int limit, int page, BoolQueryBuilder boolQueryBuilder) throws IOException {

        sort = "_score";

        SearchRequest searchRequest = new SearchRequest(tableIndex);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        searchSourceBuilder.query(boolQueryBuilder);
        searchSourceBuilder.size(limit);
        searchSourceBuilder.from(page);
        searchSourceBuilder.sort(sort, SortOrder.DESC);
        searchSourceBuilder.minScore(0.001F);
        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        return searchResponse;
}

排查方向建议

  • 优化match_phrase查询:match_phrase需要匹配词项的连续位置,再加上autocomplete分词器会生成大量n-gram词项,会大幅增加计算开销。建议换成match查询配合operator: AND,或者调整autocomplete分词器的n-gram长度范围,减少冗余词项。
  • 调整分片数量:46000条数据用5个分片,每个分片仅约9000条数据,分片过多会增加节点协调成本。建议调整为2-3个分片(符合Elasticsearch最佳实践:单分片数据量控制在10GB以内、文档数百万级)。
  • 用filter替代must处理过滤条件:is_deleted和user_id这类过滤条件放到filter子句中,filter结果会被缓存,重复查询时直接复用,减少计算量:
    boolQueryBuilder.filter(QueryBuilders.termQuery("is_deleted", 0));
    if (Strings.isNotNullOrEmpty(userId)) {
        boolQueryBuilder.filter(QueryBuilders.termQuery("user_id", String.valueOf(userId)));
    }
    
  • 优化分页方式:如果page值较大,from+size分页会导致ES在每个分片加载大量数据后再排序截断,建议换成search_after分页,避免深度分页的性能损耗。
  • 检查节点资源:查看ES节点的CPU、内存、磁盘IO使用率,CPU高负载可能是查询计算密集导致,内存不足会引发磁盘交换,严重拖慢查询。
  • 开启查询Profile:在查询中添加profile:true,查看具体执行环节的耗时分布,定位是分词、匹配还是排序等步骤占用了最多时间:
    searchSourceBuilder.profile(true);
    

内容的提问来源于stack exchange,提问作者Quang Minh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 21:40:28