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

优化方向

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

  • 使用高效的过滤条件减少不必要的查询范围。
  • 考虑是否真的需要返回全部数据,或者能否通过汇总统计或其他方式减少数据传输量。
  • 设置合理的索引策略和分片大小,优化集群配置,如增加合适的内存缓冲区等。
相关推荐
霸道流氓气质6 分钟前
ApiPost 中配置自动获取 Token 并调用业务接口完整指南
java·服务器·数据库
独隅27 分钟前
IntelliJ IDEA 接入多种AI大模型插件终极指南(2026.1 企业合规版)
java·人工智能·intellij-idea
还是奇怪1 小时前
Simon Willison 用 DSPy 优化 Datasette Agent 提示词:提示工程正在变成可测试的软件工程
java·开发语言·软件工程
zfoo-framework1 小时前
1.ansible安装 2.虚拟机克隆
java
码上解惑1 小时前
从 Dify 工作流说起:常用节点怎么选、怎样组合?
java·人工智能·ai·agent·dify·智能体·spring ai
yurenshi16681 小时前
自媒体多账号管理工具 4 款实测:聚媒通 / 融媒宝 / 新榜小豆芽 / 矩阵通横向对比,快速选型
大数据·矩阵·媒体
青山木2 小时前
Hot 100 --- 岛屿数量
java·数据结构·算法·leetcode·深度优先·广度优先
程序员-珍3 小时前
报错下载android sdk失败
android·java
nVisual3 小时前
机柜PDU安装位置与空间建模方案
大数据·网络·数据库·信息可视化·数据中心基础设施管理
糖果店的幽灵3 小时前
langgraph分支之 - 动态分支(Dynamic Branch)
java·前端·javascript·人工智能·langgraph