[JAVA] elasticsearch scrollId 很长原因

博客围绕Elasticsearch分页展开,介绍了深度分页和快照分页。因数据量大采用快照分页,在测试环境中scrollId长度超出get请求url最大长度,无法传到后台。分析了原因,如增加index会使scrollId增加,还给出了尝试性解决方案,如改get为post请求、缓存scrollId等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

elasticsearch 分页的时候有
1.深度分页(from-size) 分页的偏移值越大,执行分页查询时间就会越长
2.快照分页(scroll) 该查询会自动返回一个scrollId

由于数据量比较大. 所以采用了快照分页(scroll) 在开发环境没有任何问题. 但是在测试环境,发现scrollId的长度已经超出了get请求url的最大长度. 导致scrollId传不到后台.

于是追究其原因

1.准备环境:windows /elasticsearch-5.6.9/ 修改jvm.options [-Dfile.encoding=UTF-8] 为GBK
注意: 5.X版本和2.X版本的配置项有比较大的差异, 请自行百度

2.java代码

public class ElasticsearchTest {

    private final static String HOST = "127.0.0.1";
    private final static int PORT = 9300;
    private TransportClient client = null;

    /**
     * 获取客户端连接信息
     * 默认配置
     */
    @Before
    public void getConnect() throws UnknownHostException {
        client = new PreBuiltTransportClient(Settings.EMPTY).addTransportAddresses(
                new InetSocketTransportAddress(InetAddress.getByName(HOST), PORT));
        Logger.info("连接信息:" + client.toString());
    }

    //    @Before
    public void before() throws UnknownHostException {
        Map<String, String> map = new HashMap<>();
        map.put("cluster.name", "elasticsearch");
        Settings settings = Settings.builder().put(map).build();
        client = new PreBuiltTransportClient(settings).addTransportAddress(
                new InetSocketTransportAddress(InetAddress.getByName(HOST), PORT));
        Logger.info("连接信息:" + client.toString());
    }

    /**
     * 关闭连接
     */
    @After
    public void closeConnect() {
        if (null != client) {
            Logger.info("执行关闭连接操作...");
            client.close();
        }
    }

    /**
     * 创建索引库
     * 需求:创建一个索引库为:msg消息队列,类型为:tweet,id为1
     * 索引库的名称必须为小写
     */
    @Test
    public void addIndex1() throws IOException {
        addOne("1");
    }

    /**
     * 循环添加数据
     */
    @Test
    public void addIndex2() throws IOException {
        for (int i = 100; i < 600; i++) {
            addOne(i + "");
        }
    }

    private void addOne(String i) throws IOException {
        SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd HH:mm:sss");
        client.prepareIndex("msg", "tweet")
//        client.prepareIndex("msg", "tweet1")
//        client.prepareIndex("msg1", "tweet")
                .setSource(XContentFactory.jsonBuilder()
                        .startObject().field("name", "张三")
                        .field("date", new Date())
                        .field("fmtTime", s.format(new Date()))
                        .field("msg", "中文_" + i)
                        .endObject()).get();
    }

    /**
     * 从索引库获取数据
     */
    @Test
    public void getData1() {
        String[] indexArray = new String[0];
        SearchResponse response = client.prepareSearch(indexArray)
                .setFrom(0)
                .setSize(100)
                .addSort("date", SortOrder.ASC)
                .setScroll(new TimeValue(1000 * 60 * 60))
                .get();

        for (SearchHit searchHit : response.getHits().getHits()) {
            Logger.info(searchHit.getId() + "" + searchHit.getSourceAsString());
        }
        Logger.info("scrollId: " + response.getScrollId());
        Logger.info("scrollId.length:" + response.getScrollId().length());

    }

    /**
     * 从索引库获取数据
     */
    @Test
    public void getData2() {
        String scrollId = "1";
        SearchResponse response = client.prepareSearchScroll(scrollId)
                .setScroll(new TimeValue(1000 * 60 * 60)).get();

        for (SearchHit searchHit : response.getHits().getHits()) {
            Logger.info(searchHit.getId() + "" + searchHit.getSourceAsString());
        }
        Logger.info("scrollId: " + response.getScrollId());
        Logger.info("scrollId.length:" + response.getScrollId().length());
    }

    /**
     * 根据索引名称,类别,文档ID 删除索引库的数据
     */
    @Test
    public void deleteData() {
        DeleteResponse deleteResponse = client.prepareDelete("msg", "tweet", "1").get();

        Logger.info("deleteResponse索引名称:" + deleteResponse.getIndex()
                + "\t deleteResponse类型:" + deleteResponse.getType()
                + "\t deleteResponse文档ID:" + deleteResponse.getId()
                + "\t当前实例deleteResponse状态:" + deleteResponse.status());
    }

    @Test
    public void deleteIndex() {
        client.admin().indices().prepareDelete("msg").execute().actionGet();
        Logger.info("删除索引成功");
    }
}

这里写图片描述

分别批量增加 document , type , index 测试发现得. 当增加index的时候scrollId会增加.

解决方案(尝试):
1.改get请求为post请求.但是scrollId的长度达到3w+字符.每次需要夹带几KB的数据在请求中,而且scrollId可能将来会不断变大
2.将scrollId缓存,设置过期时间.每次请求下一次查询后清除该scrollId的缓存.

博友还有什么新的解决方案可以在我的博客下留言.

<think>嗯,用户想了解Elasticsearch的分页实现方法及参数说明。我之前学过一些ES的基础知识,但分页这部分可能还需要仔细回忆一下。首先,Elasticsearch常用的分页方式有三种:From + Size、Scroll和Search After。用户可能需要知道每种方法的适用场景和具体用法。 记得From + Size是最常见的,类似于传统数据库的分页,不过当深度分页时性能会下降,因为需要从每个分片获取大量数据,然后协调节点汇总排序。比如用户要查第1000页,每页10条,ES实际要处理10000条数据,这可能导致内存和性能问题。所以需要提醒用户注意深度分页的限制,默认最大10000条,可以通过设置index.max_result_window调整,但不太推荐。 然后是Scroll查询,适合大批量数据导出或离线处理。它类似于快照,会保留当时的索引状态,但会占用资源,特别是如果有很多打开的Scroll上下文,可能会影响集群性能。需要提到Scroll_id的有效期,以及用完及时清理的重要性。 接着是Search After,这是推荐用于深度分页的方法。它基于上一页的排序值来获取下一页,避免了性能问题。但需要确保排序字段唯一,否则可能漏掉数据。比如用时间戳加ID作为排序条件。这种方法适合实时分页,且不需要维护上下文,资源消耗较少。 参数方面,From + Size中的from和size参数需要配合使用,Scroll的scroll参数设置有效期,Search After需要sort参数和search_after值。还要提到Java API中的实现方式,比如使用prepareSearch方法设置from和size,或者使用SearchScrollRequestBuilder处理Scroll查询。 可能用户在实际使用中遇到了深度分页的性能问题,或者需要处理大量数据的分页导出。需要对比这三种方法的优缺点,帮助用户根据场景选择合适的方法。例如,实时分页用Search After,大数据量导出用Scroll,小规模数据用From + Size。 还要注意引用中的内容,比如引用[3]提到三种分页方式,引用[4]提到协调节点处理数据的方式,只返回_id和_score以减少数据传输量。这些都可以作为支持论据,增强回答的可信度。 用户可能还需要知道如何在实际代码中实现这些分页方法,比如Java或Python的示例。不过用户的问题主要是参数说明和方法,所以重点放在参数和使用场景上,附带的代码示例可以更直观。 最后,生成相关问题时要围绕分页的优化、性能比较、实际应用案例和与其他数据库的对比,这样用户可以有进一步的学习方向。</think>Elasticsearch 分页实现方法及参数说明可分为以下三种核心方案,适用于不同场景: ### 一、From + Size 分页(浅分页) **实现原理**: 通过`from`参数指定起始偏移量,`size`参数指定每页数量。例如查询第3页(每页10条): $$from = 20,\ size = 10$$ **参数说明**: - `from`:起始文档位置(默认0) - `size`:每页返回文档数(默认10) **代码示例**(Java API): ```java SearchResponse response = client.prepareSearch("index") .setFrom(20) .setSize(10) .get(); ``` **限制**: 深度分页时性能急剧下降(如`from=10000`),因需在所有分片计算前$N$条数据再汇总排序[^4]。默认最大支持`index.max_result_window=10000`,可调整但易引发内存溢出。 **适用场景**: 小规模数据($N < 1000$)的实时查询。 --- ### 二、Scroll 分页(深分页) **实现原理**: 创建数据快照,通过迭代`scroll_id`遍历结果集。类似数据库游标。 **参数说明**: - `scroll`:指定上下文保留时间(如`1m`) - `size`:每次滚动获取的文档数 **代码示例**(首次查询): ```java SearchResponse response = client.prepareSearch("index") .setSize(100) .setScroll(TimeValue.timeValueMinutes(5)) .get(); String scrollId = response.getScrollId(); // 后续请求使用此ID ``` **限制**: - 快照数据无法实时更新 - 长时间占用内存资源 - 需手动清理`scroll_id`[^3] **适用场景**: 离线导出全量数据(如日志备份)。 --- ###
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值