ES分页查询的最佳实践:三种方案

Elasticsearch(ES)中进行分页查询时,最佳实践取决于具体的使用场景和需求。

以下是对每种分页方法的简要分析以及它们适用的情况:

1. From + Size

  • 最常见且直观的方法,通过from参数指定跳过多少条记录,size参数指定每次返回多少条记录。
  • 优点:实现简单,适用于小规模或浅层分页,即前几页查询。
  • 缺点 :随着from值增大,查询效率会显著降低,尤其是在深度分页的情况下(例如,查询很多页之后的数据),因为ES需要遍历所有之前的结果才能找到指定偏移的结果集,这对分布式系统来说成本非常高。

2. Scroll API

  • 提供了一种持续检索大量数据的方式,创建一个"滚动"上下文,可以在一段时间内保持一致性视图。
  • 优点:非常适合大数据量的批量读取或深度分页,尤其是在不需要考虑数据实时更新的情况下,如数据导出或批处理任务。
  • 缺点:滚动上下文会占用服务器资源,且对实时性要求高的场景不合适,因为它反映的是某个时间点的快照状态,不能反映出滚动上下文创建后数据的变化。
  • 从ES 5.0版本开始提供,用于克服from+size在深度分页时的性能瓶颈。
  • 优点 :利用 _score 或用户定义的排序字段来进行连续查询,避免了大规模跳跃式分页的问题。相比from+size,它在深度分页时性能更优,同时能够更好地处理实时变化的数据。
  • 缺点 :需要有稳定的排序字段,并且不是所有场景下都能方便地转换为search_after模式。

代码示例

java 复制代码
package org.example;

import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

public class ESScrollMain {
    private static final String indexName = "kibana_sample_data_logs";

    public static void main(String[] args) throws IOException {
        System.out.println("Hello and welcome!");
        RestClientBuilder builder = RestClient.builder(new HttpHost("10.x.x.x", 9200, "http"));
        RestHighLevelClient client = new RestHighLevelClient(builder);

        SearchRequest searchRequest = new SearchRequest(indexName);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // hit 返回值(bool 查询返回条数)
//        searchSourceBuilder.size(0);
//        searchSourceBuilder.from(0);
        searchSourceBuilder.trackTotalHits(true);
        // 超时时间60s
        MatchAllQueryBuilder search = QueryBuilders.matchAllQuery();
        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        searchSourceBuilder.size(2000);
        searchSourceBuilder.query(search);

        long scrollTime = 30L;
        searchRequest.source(searchSourceBuilder);
        searchRequest.scroll(TimeValue.timeValueSeconds(scrollTime));

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

        String scrollId = searchResponse.getScrollId();
        SearchHit[] hits = searchResponse.getHits().getHits();

        int count = 0;
        int batch = 1;
        System.out.println("初始结果条数:" + count);
        count += hits.length;
        System.out.println("滚动第" + batch + "批结果总条数:" + count);
        while (hits != null && hits.length > 0) {
            batch++;
            SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
            scrollRequest.scroll(TimeValue.timeValueSeconds(scrollTime));
            searchResponse = client.scroll(scrollRequest, RequestOptions.DEFAULT);
            scrollId = searchResponse.getScrollId();
            hits = searchResponse.getHits().getHits();
            count += hits.length;
            System.out.println("滚动第" + batch + "批结果总条数:" + count);

        }

        System.out.println("结束,总计:"+searchResponse.getHits().getTotalHits());

    }
}

综合考虑

  • 对于网页应用中的普通分页浏览,尤其是前几页,from+size足够。
  • 如果需要处理大数据集且允许一定的延迟,或者一次性获取所有结果,Scroll API 是更好的选择。
  • 对于深度分页且需要实时性较好的场景,应优先考虑search_after

优化方向

此外,针对大型分页查询的性能优化还可以包括:

  • 使用高效的过滤条件减少不必要的查询范围。
  • 考虑是否真的需要返回全部数据,或者能否通过汇总统计或其他方式减少数据传输量。
  • 设置合理的索引策略和分片大小,优化集群配置,如增加合适的内存缓冲区等。
相关推荐
廋到被风吹走1 小时前
【Spring】Spring Data JPA Repository 自动实现机制深度解析
java·后端·spring
MX_93591 小时前
Spring中Bean的配置(一)
java·后端·spring
sg_knight5 小时前
Spring 框架中的 SseEmitter 使用详解
java·spring boot·后端·spring·spring cloud·sse·sseemitter
郑州光合科技余经理7 小时前
同城系统海外版:一站式多语种O2O系统源码
java·开发语言·git·mysql·uni-app·go·phpstorm
一只乔哇噻7 小时前
java后端工程师+AI大模型开发进修ing(研一版‖day60)
java·开发语言·人工智能·学习·语言模型
千里码aicood7 小时前
计算机大数据、人工智能与智能系统开发定制开发
大数据·人工智能·深度学习·决策树·机器学习·森林树
Dolphin_Home7 小时前
笔记:SpringBoot静态类调用Bean的2种方案(小白友好版)
java·spring boot·笔记
MetaverseMan8 小时前
Java虚拟线程实战
java
浪潮IT馆9 小时前
Tomcat运行war包的问题分析与解决步骤
java·tomcat
悟能不能悟9 小时前
Caused by: java.sql.SQLException: ORA-28000: the account is locked怎么处理
java·开发语言