ElasticSearch DSL语句之深度分页问题

ops/2025/1/20 7:22:26/

Elasticsearch 在处理分页查询时,存在一个常见问题——深度分页问题。这一问题不仅存在于 Elasticsearch 中,传统数据库在进行分页查询时也会遇到类似的问题。由于 Elasticsearch 存储的数据量通常比传统数据库更大,因此深度分页问题在 Elasticsearch 中更加突出。



一、深度分页问题的产生

1. 数据分片存储

为了能够存储海量数据,Elasticsearch 将数据分片存储,即将数据分成多个片段存储在不同的节点上。比如说,当一个索引库包含数亿条数据时,为了有效存储,Elasticsearch 会将这些数据分成多个分片(shard),每个分片存储在不同的节点上。举例来说,假如有一个索引库,包含大量数据,它可能会将数据分成 4 个分片存储,每个分片存储一部分数据。


2. 分页查询中的问题

在 Elasticsearch 中进行分页查询时,涉及到多个分片上的数据。例如,假设我们查询第 100 页的数据,每页 10 条。那么查询的 from 参数将是 990,即要从第 990 条数据开始查询。这意味着我们要查找前 1000 条数据中的最后 10 条。



问题就出现在这里。为了找到第 1000 条数据及之后的数据,Elasticsearch 必须先对数据进行排序,然后从每个分片中查找出前 1000 条数据,再从中筛选出第 990 到 1000 条数据。这就导致了以下问题:

  1. Elasticsearch 必须先对整个数据集进行排序,才能找到目标数据。
  2. 在分片存储的情况下,Elasticsearch 需要从每个分片中查找前 1000 条数据,虽然每个分片上的数据存储顺序是随机的,因此无法保证每个分片的前 250 条数据就能合并为全局的前 1000 条数据。

通过这种方式,查找前 1000 条数据时,性能会非常低,尤其是数据量越大,查询越慢。


3. 深度分页带来的影响

如果我们查询的数据页码更深(如查询第 1000 页),问题就变得更加严重。为了查找第 1000 页的数据,Elasticsearch 需要从每个分片中查询前 10 万条数据(每页 10 条,查询 1000 页),并合并排序后再筛选出需要的数据。这会消耗大量的计算资源和内存,导致查询性能下降,甚至可能出现内存溢出等问题。



二、深度分页的解决方案

为了应对深度分页问题,Elasticsearch 提供了两种解决方案:

  1. Search After:推荐的解决方案
  2. Scroll:已被弃用,不再推荐

1. Search After 方案

Search After 解决方案的原理非常简单。其基本思路是:每次分页查询时,记录上一页最后一条数据的排序值,然后在下一页查询时,将这个排序值作为查询条件,限定只查询大于该值的数据。这种方式避免了传统分页查询中需要计算整个数据集的排序和分页,从而有效减少了查询时的性能消耗。


2. Search After 的实现

Search After 方案的实现可以分为以下几步:

  • 排序:首先对数据进行排序,确保查询结果有一个明确的顺序。
  • 记住上一页的最后一条数据的排序值:每次查询完一页数据后,记录上一页最后一条数据的排序值。
  • 下一次查询时使用排序条件:在下一次查询时,使用上一页最后一条数据的排序值作为条件,只查询比该值大的数据。

举个简单的例子,假设我们查询的排序字段是 score,每次查询时,记录上一页最后一条记录的 score 值。下一次查询时,我们会设置条件,查询所有 score 大于上一页最后一条记录的 score 的数据。

这样,每次查询都可以只从排序值的起始点查询,而不需要从数据的开头重新排序。这样一来,查询性能得到大幅提升。


3. 使用场景

Search After 适用于以下场景:

  • 深度分页:比如需要分页查询大量数据时,可以有效避免深度分页带来的性能问题。
  • 数据迁移:例如在将 Elasticsearch 中的数据迁移到新的索引时,需要顺序读取所有数据并进行迁移,这时可以使用 Search After 进行顺序读取。
  • 滚动查询:例如一些应用场景需要不断向下滚动加载数据(如社交媒体应用、推荐系统等),Search After 可以非常有效地解决这些场景中的分页问题。

4. Search After 的局限性

尽管 Search After 解决了深度分页的问题,但它也有一些局限性:

  • 只能顺序查询:Search After 不支持跳页查询,只能顺序查询。也就是说,你不能从第 10 页直接跳到第 100 页。
  • 需要排序字段:Search After 需要一个明确的排序字段。否则,就无法确定每一页的查询起点。



三、传统分页的使用限制

虽然 Elasticsearch 提供了 Search After 方案来解决深度分页问题,但在一些情况下,我们依然会使用传统的分页方式。传统的分页方式(即使用 fromsize 参数)适合于小规模的分页查询或当页码不太深时。为了避免查询性能过低,可以为查询设置分页的上限。例如,在 Elasticsearch 中,fromsize 的最大值通常被限制为 10000 条数据,超过这个限制会返回错误。


1. 传统分页的场景

传统分页适用于以下场景:

  • 小规模分页查询:当数据量较小,或者页码不深时,使用 fromsize 进行分页查询是非常高效的。
  • 随机跳页查询:当用户需要跳转到指定页码时,传统分页的方式更适合。

2. 传统分页的限制

为了防止深度分页带来的性能问题,传统分页(即 fromsize)通常会对最大查询页数进行限制。例如,设置 from + size 的最大值为 10000。超过这个限制,查询会返回错误。



四、总结

  • 深度分页问题:随着页码的增加,传统分页查询(fromsize)的性能会大幅下降,尤其是在数据量巨大的情况下。
  • Search After:Elasticsearch 提供的解决方案,避免了重新排序数据,改为通过记住上一页最后一条记录的排序值,顺序查询下一页数据。
  • 使用场景:Search After 适用于深度分页、数据迁移和滚动查询等场景。
  • 传统分页:适合于小规模查询和随机跳页查询,但在数据量过大时需要限制查询页数,以避免性能问题。

http://www.ppmy.cn/ops/151607.html

相关文章

Spring的IoC、Bean、DI的简单实现,难度:※※※

目录 场景描述 第一步:初始化Maven项目 第二步:Maven导入Spring包(给代码) 第三步:创建Spring配置文件 第四步 创建Bean 第五步 简单使用Bean (有代码) 第六步 通过依赖注入使用Bean&…

RocketMQ中的一些细节

1.前言 此文章是在儒猿课程中的学习笔记,感兴趣的想看原来的课程可以去咨询儒猿课堂《从0开始带你成为RocketMQ高手》,我本人觉得这个作者还是不错,都是从场景来进行分析,感觉还是挺适合我这种小白的。这块主要都是我自己的学习笔…

Web第一次作业

目录 题目 html代码 index login register css代码 base index login register 效果展示 index login register 题目 实现一个登录页面、实现一个注册页面;实现一个主页 - 登录页面:login.html - 注册页面:register.html - 主页…

Spring 连问夺魂

1、说说Spring里用了哪些设计模式? 单例模式:Spring 中的 Bean 默认情况下都是单例的。无需多说。 工厂模式:工厂模式主要是通过 BeanFactory 和 ApplicationContext 来生产 Bean 对象。 代理模式:最常见的 AOP 的实现方式就是通过代理来…

第6章:Python TDD实例变量私有化探索

写在前面 这本书是我们老板推荐过的,我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后,我突然思考,对于测试开发工程师来说,什么才更有价值呢?如何让 AI 工具更好地辅助自己写代码,或许…

医院挂号就诊系统设计与实现(代码+数据库+LW)

摘 要 传统办法管理信息首先需要花费的时间比较多,其次数据出错率比较高,而且对错误的数据进行更改也比较困难,最后,检索数据费事费力。因此,在计算机上安装医院挂号就诊系统软件来发挥其高效地信息处理的作用&#…

多租户支持与企业级应用场景:ShardingSphere 的应用解析

随着企业业务的多样化和复杂性增加,多租户架构成为支持多个独立用户或租户在同一系统上高效、安全地运行的关键方案。ShardingSphere 提供了强大的多租户支持,使得在分布式数据库环境下,多个租户可以共享资源,同时保证数据隔离与安…

学习ASP.NET Core的身份认证(基于JwtBearer的身份认证5)

用户在前端页面登录成功后会从服务端获取Token,后续调用服务器的服务接口时都得带着Token,否则就会验证失败。之前使用postman测试的时候,获取Token后再调用其它服务都是人工将Token添加到Header中,网页中没法这么做,只…