Elasticsearch中的三种分页策略深度解析:原理、使用及对比

码到三十五 : 个人主页

在Elasticsearch中,分页是查询操作中不可或缺的一部分。随着数据量的增长,如何高效地分页查询数据急需需要面对的问题。Elasticsearch提供了三种主要的分页方式:from + sizescrollsearch_after。下面详细介绍这三种分页方式的特点和使用场景。

目录

    • 方式一:from + size
      • 实现原理
      • 使用方式
      • 优点
      • 缺点
      • 使用场景
    • 方式二:scroll
      • 实现原理
      • 使用方式
      • DSL 代码示例
      • 优点
      • 缺点
      • 使用场景
    • 方式三:search_after
      • 实现原理
      • 使用方式
      • 优点
      • 缺点
    • 使用场景
    • 三种方式总结
    • 结语

在这里插入图片描述

方式一:from + size

from + size是Elasticsearch中最直观的分页方式。其中,from参数表示从第几条记录开始返回,size参数表示返回的记录数。

实现原理

from + size 分页方式的原理相对简单。当你执行一个搜索查询并指定了 fromsize 参数时,Elasticsearch 会进行以下步骤:

  1. 分发查询:Elasticsearch会将查询请求分发到所有相关的分片上。
  2. 查询分片:每个分片都会执行查询,并返回前 from + size 条符合条件的文档(但实际上只会用到最后的 size 条)。
  3. 合并和排序:协调节点(通常是执行搜索的Elasticsearch节点)会收集所有分片返回的结果,将它们合并成一个全局的结果集,并根据查询中指定的排序规则进行排序。
  4. 截断和返回:然后,协调节点会从排序后的结果集中截取从 from 位置开始的 size 条记录,并将它们返回给客户端。

由于 from + size 需要合并和排序所有分片返回的结果,因此当 from 值很大时,这个过程可能会变得非常慢,因为它需要处理大量的数据。

使用方式

在Elasticsearch中,使用fromsize进行分页查询的DSL(Domain Specific Language):

GET /your_index/_search
{
    "query": {
        "match_all": {}  // 这里可以替换为任何你需要的查询条件
    },
    "from": 0,           // 从第几条记录开始,索引从0开始
    "size": 10,          // 返回的记录条数
    "sort": [
        { "field_name": {"order": "asc"}}  // 可选,根据某个字段进行排序
    ]
}

from参数指定了从哪一条记录开始返回,size参数指定了要返回的记录条数。

假设一个名为products的索引,搜索名称中包含"apple"的产品,并且从第10条记录开始返回10条结果,按价格升序排序:

GET /products/_search
{
    "query": {
        "match": {
            "name": "apple"
        }
    },
    "from": 9,  // 注意,索引从0开始,所以第10条记录的索引是9
    "size": 10,
    "sort": [
        { "price": {"order": "asc"}}
    ]
}

from设置为9以跳过前9条记录,size设置为10以返回接下来的10条记录,并且结果按照price字段的升序排列。

Elasticsearch会返回如下响应:

{
  "took": 5,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 100,  // 假设总共有100条符合查询条件的产品
      "relation": "eq"
    },
    "max_score": 1.0,
    "hits": [
      {
        "_index": "products",
        "_type": "_doc",  // 注意:在Elasticsearch 7.x及之后的版本中,_type字段通常被设置为"_doc"
        "_id": "10",
        "_score": 1.0,
        "_source": {
          "name": "Apple iPhone 12",
          "price": 999.99,
          // ... 其他字段
        }
      },
      // ... 其他9条产品的结果
      {
        "_index": "products",
        "_type": "_doc",
        "_id": "19",
        "_score": 1.0,
        "_source": {
          "name": "Apple Watch Series 6",
          "price": 399.99,
          // ... 其他字段
        }
      }
    ]
  }
}

优点

  • 直观易用:开发者可以很容易地指定要返回的记录范围和数量。
  • 实时性:适用于实时搜索场景,可以立即获取最新的查询结果。

缺点

  • 性能问题:当from值很大时,Elasticsearch需要遍历大量数据才能找到起始位置,然后返回size条记录。这会导致查询性能下降,尤其是在数据量很大的情况下。
  • 资源消耗:深度分页会消耗大量CPU和内存资源,对集群性能造成压力。

在这里插入图片描述

使用场景

适用于数据量不大、实时性要求高的场景。

方式二:scroll

scroll是一种基于游标的分页方式,它允许我们遍历大量数据而不需要在每次请求时重新计算整个搜索。

实现原理

scroll 分页方式的原理与游标(cursor)类似。当你执行一个带有 scroll 参数的搜索查询时,Elasticsearch 会:

  1. 初始化搜索上下文:Elasticsearch会为这次搜索创建一个快照(snapshot),并存储相关的搜索上下文(search context)。这个上下文包括查询本身、排序方式、聚合等所有与搜索相关的信息。
  2. 返回初始结果:然后,Elasticsearch会像普通搜索一样返回第一批结果,并附带一个 scroll_id。这个 scroll_id 是唯一标识这次搜索上下文的。
  3. 使用 scroll_id 获取更多结果:客户端可以使用这个 scroll_id 来请求更多的结果。Elasticsearch会基于之前存储的搜索上下文,从快照中检索更多的结果,并返回给客户端。这个过程可以重复多次,直到所有的结果都被检索完或搜索上下文过期。

由于 scroll 只需要在开始时计算一次搜索上下文,并在之后基于这个上下文来获取结果,因此它在处理大量数据时通常比 from + size 更快。但是,它也会消耗更多的服务器资源来维护搜索上下文和快照。

使用方式

在Elasticsearch中,scroll是一种用于检索大量数据(可能是数百万条记录)的分页机制,它允许你保持一个搜索的“上下文”并继续检索结果,而不需要为每一页都重新计算整个搜索。以下是使用scroll进行分页的DSL代码示例:

DSL 代码示例

// 初始化scroll搜索
POST /_search/scroll
{
    "size": 100,           // 每次返回的文档数量
    "scroll": "1m",        // 保持scroll上下文的活动时间,这里是1分钟
    "query": {
        "match_all": {}    // 可替换为任何需要的查询条件
    }
}

// 后续的scroll请求(在第一次请求返回后)
POST /_search/scroll
{
    "scroll": "1m",        // 保持与第一次请求相同的scroll上下文时间
    "scroll_id": "你的scroll_id" // 第一次请求返回的scroll_id
}

说明

  1. 首次POST /_search/scroll请求会返回一部分结果(基于size参数)以及一个scroll_id
  2. 使用这个scroll_id,你可以通过后续的POST /_search/scroll请求来获取更多的结果。
  3. scroll参数定义了在多长时间内可以保持scroll上下文有效。如果在这个时间内没有新的scroll请求,那么scroll上下文就会被删除,无法再获取更多结果。

响应结果

第一次请求会返回如下结果:

{
  "_scroll_id": "DnF1ZXJ5THV6QXRlbl84791547351",
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1000,
      "relation": "eq"
    },
    "max_score": 1.0,
    "hits": [
      {
        "_index": "your_index",
        "_type": "_doc",
        "_id": "1",
        "_score": 1.0,
        "_source": {
          // ... 文档的源数据 ...
        }
      },
      // ... 其他文档 ...
    ]
  }
}

在这里插入图片描述

响应中可以看到_scroll_id字段,这个值需要用于后续的scroll请求。

后续的scroll请求

使用上面响应中的_scroll_id进行后续的scroll请求:

POST /_search/scroll
{
    "scroll": "1m",
    "scroll_id": "DnF1ZXJ5THV6QXRlbl84791547351"
}

这个请求会返回下一批文档,直到所有的文档都被检索完或者scroll上下文过期。

根据你的Elasticsearch集群的实际设置和性能需求来调整sizescroll参数的值。

优点

  • 高效性:scroll会维护一个游标,通过游标来获取下一批数据,而不是重新计算整个搜索。这使得scroll在处理大量数据时更加高效。
  • 实时性:scroll可以获取到查询发起时刻的数据快照,并在整个scroll过程中保持这个快照。这意味着在scroll过程中,即使有新数据写入,也不会被包含在查询结果中。

缺点

  • 非实时性:由于scroll是基于数据快照的,因此它不适用于需要实时获取最新数据的场景。
  • 资源消耗:scroll会消耗大量的服务器资源来维护游标和数据快照,因此需要谨慎使用。

使用场景

适用于需要遍历大量数据、非实时性要求高的场景,如日志导出、数据迁移等。

方式三:search_after

search_after是一种基于排序值的分页方式,它允许我们根据上一页的最后一条数据的排序值来获取下一页的数据。

实现原理

search_after 分页方式的原理是基于上一次查询的结果来确定下一次查询的起始位置。当你执行一个带有 search_after 参数的搜索查询时,Elasticsearch 会:

  1. 排序和返回结果:首先,Elasticsearch会像普通搜索一样执行查询,并根据指定的排序字段对结果进行排序。然后,它会返回第一批结果。
  2. 确定下一次查询的起始位置:客户端可以选择结果集中的任意一条记录作为下一次查询的起始位置。这通常是通过记录该条记录的排序字段值来实现的。
  3. 使用 search_after 获取更多结果:在下一次查询时,客户端会指定 search_after 参数,并将上一次查询的起始位置(即排序字段值)作为该参数的值。Elasticsearch会基于这个值来确定下一次查询的起始位置,并返回该位置之后的结果。

由于 search_after 不需要像 from + size 那样合并和排序所有分片返回的结果,也不需要像 scroll 那样维护搜索上下文和快照,因此它在深度分页时通常比这两种方式更高效。但是,它要求排序字段的值必须是唯一的,以确保能够准确地确定下一次查询的起始位置。

使用方式

有一个名为products的索引,它包含产品的信息,想要根据产品的价格和上架时间进行分页查询。

1. 索引结构

products索引有以下的字段结构:

  • product_id (keyword类型,作为文档的唯一标识)
  • price (float或scaled_float类型,表示产品价格)
  • created_at (date类型,表示产品上架时间)

2. 初始查询(没有search_after

首先执行一个初始查询来获取第一页的结果,并基于price(降序)和created_at(升序)进行排序。

GET /products/_search
{
    "size": 10,
    "query": {
        "match_all": {}  // 或者你可以添加具体的查询条件
    },
    "sort": [
        { "price": {"order": "desc"}},
        { "created_at": {"order": "asc"}}
    ]
}

3. 处理响应并准备search_after参数

从响应中可以获取最后一篇文档的排序字段值(即pricecreated_at的值)。这些值将用于下一页的search_after请求。

响应中的最后一个文档:

{
    "_index": "products",
    "_type": "_doc",
    "_id": "最后一个产品的ID",
    "_score": null,
    "_sort": [
        129.99,  // 最后一个产品的price值
        "2023-10-23T12:00:00Z"  // 最后一个产品的created_at值
    ],
    "_source": {
        // ... 产品的详细信息 ...
    }
}

将这些_sort字段的值(即129.99"2023-10-23T12:00:00Z")作为下一页请求中的search_after参数。

4. 使用search_after进行下一页查询

使用search_after来请求下一页的数据:

GET /products/_search
{
    "size": 10,
    "query": {
        "match_all": {}  // 保持与初始查询相同的查询条件
    },
    "sort": [
        { "price": {"order": "desc"}},
        { "created_at": {"order": "asc"}}  // 保持与初始查询相同的排序字段和顺序
    ],
    "search_after": [
        129.99,  // 上一页最后一个产品的price值
        "2023-10-23T12:00:00Z"  // 上一页最后一个产品的created_at值
    ]
}

5. 重复以上步骤以获取更多页

可以继续执行上述步骤来获取更多的页面,直到没有更多的结果返回为止。记得每次都要使用上一页最后一个文档的排序字段值来设置search_after参数。

优点

  • 高效性:相比from + sizesearch_after在深度分页时更加高效。因为它不需要像from + size那样获取并排序大量的数据,而只需要根据排序值获取下一页的数据。
  • 灵活性:search_after允许我们跳过中间的页面,直接获取指定位置的数据。

缺点

  • 依赖排序字段:search_after需要依赖一个或多个排序字段来确定下一页的位置。如果排序字段的值不是唯一的,可能会导致查询结果不准确。
  • 实时性:虽然search_afterscroll更实时,但它仍然无法获取到查询发起后的最新数据。

使用场景

适用于需要深度分页、实时性要求相对较高、且排序字段唯一的场景。

三种方式总结

  1. from + size(浅分页)

    • 原理:通过指定from(起始偏移量)和size(每页大小)来分页。默认from为0,size为10。
    • 优点:简单直观,易于理解。
    • 缺点:
      • from值很大时,性能会显著下降,因为Elasticsearch需要从每个分片中获取指定数量的文档,然后在协调节点进行全局排序以获取最终的结果。这会导致大量的网络传输和CPU/内存消耗。
      • 不适合处理大量数据或深度分页的情况。
    • 适用场景:适用于数据量较小或不需要深度分页的场景。
  2. scroll

    • 原理:类似于数据库中的游标,通过保持一个滚动上下文来获取大量数据。每次请求会返回一个scroll_id,用于获取下一页数据。
    • 优点:
      • 适用于需要获取大量数据(如数据导出)的场景。
      • 可以保持滚动上下文,无需在每次请求时重新计算。
    • 缺点:
      • 滚动上下文会占用服务器资源,如果长时间不关闭,可能会导致资源耗尽。
      • 不支持随机访问页面,只能顺序获取数据。
      • 默认情况下,scroll请求会保持一段时间(如1分钟)的上下文,如果在这段时间内没有新的请求,上下文将被自动清除。
    • 适用场景:适用于需要按顺序获取大量数据的场景,如数据导出。
  3. search_after

    • 原理:通过指定上一页最后一个文档的排序值来获取下一页数据。需要配合sort字段使用。
    • 优点:
      • 在深度分页时性能较好,因为它避免了全局排序和大量网络传输。
      • 可以随机访问页面。
    • 缺点:
      • 需要确保每次请求都使用相同的排序字段和顺序。
      • 如果排序字段的值发生更改(如文档被更新或删除),可能会导致结果不一致。
    • 适用场景:适用于需要深度分页或随机访问页面的场景。

在这里插入图片描述

选择哪种分页方式取决于你的具体需求和场景。对于大多数常见的分页需求,from + size(浅分页)可能足够使用。但是,如果你需要处理大量数据或进行深度分页,那么scrollsearch_after可能是更好的选择。

结语

在选择Elasticsearch的分页方式时,需要根据具体的需求和使用场景来权衡各种方式的优缺点。from + size适用于数据量不大、实时性要求高的场景;scroll适用于需要遍历大量数据、非实时性要求高的场景;而search_after则适用于需要深度分页、实时性要求相对较高、且排序字段唯一的场景。通过合理使用这些分页方式,可以提高Elasticsearch的查询性能,更好地满足业务需求。


更多深度内容...请关注公众号,纯技术,纯干货 !

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/603726.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ICode国际青少年编程竞赛- Python-2级训练场-基础训练4

ICode国际青少年编程竞赛- Python-2级训练场-基础训练4 1、 for i in range(4):if i > 2:Flyer[i].step(3)else:Flyer[i].step(1) Dev.step(Item[3].x - Dev.x)2、 for i in range(6):if i < 3:Flyer[i].step(2)else:Flyer[i].step(3) Dev.step(Item[2].x - Dev.x)3、 …

制造版图大变革!逾10座晶圆厂蓄势待发 | 百能云芯

在全球半导体产业的激烈竞争和市场需求的复杂波动中&#xff0c;晶圆厂建设热潮正在美国兴起&#xff0c;这一波建设浪潮的核心动力之一&#xff0c;便是美国政府推出的《芯片与科学法案》所承诺的巨额补贴&#xff0c;旨在提升美国在全球半导体行业的竞争力。 当地时间4月25日…

翻译《The Old New Thing》 - The new scratch program

The new scratch program - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20050422-08/?p35813 Raymond Chen 2005年4月22日 译注&#xff1a;此篇是 翻译《The Old New Thing》 - The scratch program 姊妹篇&#xff0c;对 scratch 程序作…

普通人副业要趁早,5种靠谱且持久的赚钱副业

中年危机、35岁被裁&#xff0c;这些听起来就让人焦虑的词汇&#xff0c;是否也让你感到不安&#xff1f;别担心&#xff0c;只要你早早开启副业之旅&#xff0c;这些都不是问题。 今天&#xff0c;我要为你介绍的这5种副业&#xff0c;不仅能帮你赚钱&#xff0c;还能让你的能…

前端高频面试题 5.08

事件委托 事件委托是前端开发中常用的一种优化性能和代码可维护性的方法&#xff0c;它基于DOM的事件冒泡机制。当一个元素触发事件时&#xff0c;这个事件会按照从顶层到底层的顺序传播&#xff0c;直到最底层的元素&#xff08;通常是文档的根节点&#xff09;。事件委托利用…

张大哥笔记:如果不想继续打工,互联网创业或许是最好的出路!

互联网时代最好的出路&#xff0c;就是选择创业&#xff0c;不要选择打工。选择打工很亏&#xff0c;你学到的是打工的本事。而创业&#xff0c;看似不赚钱&#xff0c;看似倒霉&#xff0c;但是会锻炼出了你一天赚几千&#xff0c;甚至几万的本事。 随着互联网越来越被人们所…

Educational Codeforces Round 165 (Div. 2) A~E

A.Two Friends (思维) 题意&#xff1a; 小 A A A想开一个派对。他有 n n n个朋友&#xff0c;他希望至少有 2 2 2个朋友参加他的派对。 i i i 这个朋友最好的朋友是 p i p_i pi​ 。所有的 p i p_i pi​ 都是不同的&#xff0c;对于每一个 i ∈ [ 1 , n ] i \in [1, n] …

C++之泛型编程---有限双端队列结构容器

引言 为了解决工业领域代码容器的通用化&#xff0c;可以考虑C里的泛型编程概念。假设一个场景需要实时保存最近的n个数据并按照顺序依次处理时&#xff0c;就需要定义一种新的容器来满足要求。当容器不满时&#xff0c;添加数据直接到队尾&#xff0c;当容器数据已经为n个时&a…

毕设UI设计不会前端怎么办?今天看到了一款自动生成UI的项目-OpenUI

试用地址&#xff1a;Create a new Elemint (openui.fly.dev) OpenUI 是由 W&B 开发开源项目&#xff0c;旨在简化用户界面(UI)组件的构建过程。它通过允许开发者使用想象力描述 UI&#xff0c;然后实时看到渲染效果&#xff0c;使得 UI 开发变得有趣、快速且灵活。 这个…

CSS-盒子模型元素溢出

作用&#xff1a;控制溢出的元素的内容的显示方式 属性&#xff1a;overflow 属性值 属性值效果hidden溢出隐藏scroll溢出滚动&#xff08;无论是否溢出&#xff0c;都显示滚动条位置&#xff09;auto溢出滚动&#xff08;溢出才显示滚动条位置&#xff09; <!DOCTYPE html&…

npm无法安装node-sass 的问题

安装 node-sass 的问题呈现&#xff1a;4.9.0版本无法下载 Downloading binary from https://github.com/sass/node-sass/releases/download/v4.9.0/win32-x64-72_binding.node Cannot download "https://github.com/sass/node-sass/releases/download/v4.9.0/win32-x64-…

Pytorch学习笔记——卷积操作

一、认识卷积操作 卷积操作是一种数学运算&#xff0c;它涉及两个函数&#xff1a;输入函数&#xff08;通常是图像&#xff09;和卷积核&#xff08;也称为滤波器或特征检测器&#xff09;。卷积核在输入函数上滑动&#xff0c;将核中的每个元素与其覆盖的输入函数区域中的对应…

华为数据之道第四部分导读

目录 导读 第四部分 第10章 未来已来&#xff1a;数据成为企业核心竞争力 数据&#xff1a;新的生产要素 数据被列为生产要素&#xff1a;制度层面的肯定 数据将进入企业的资产负债表 数据资产的价值由市场决定 大规模数据交互的企业数据生态 数据生态离不开底层技术的…

618大促买什么数码好物最划算?必囤不后悔好物清单来了!

随着年度618购物盛宴的临近&#xff0c;作为数码领域的资深狂热者&#xff0c;满怀激情与憧憬为大家精心挑选了一系列令人瞩目的数码产品。无论你是热衷于追逐最新科技潮流的先锋&#xff0c;还是期望通过数码设备提升生活品质的优雅用户&#xff0c;这里都定有一款能触动你内心…

(动画详解)LeetCode20.有效的括号

题目描述 20. 有效的括号 - 力扣&#xff08;LeetCode&#xff09; 解题思路 栈的方法 遍历整个字符串 当检测到左括号的时候&#xff0c;就让左括号入栈 当检测到右括号的时候&#xff0c;就让左括号出栈与右括号对比 如果相等则继续比较直到结束&#xff0c;如果不相等…

在Linux中安装Docker

如果之前安装过旧版本的 Docker&#xff0c;可以使用下面命令卸载&#xff1a; yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-selinux \docker-engine-selinux \docker-engine…

[华为OD]C卷 BFS 亲子游戏 200

题目&#xff1a; 宝宝和妈妈参加亲子游戏&#xff0c;在一个二维矩阵&#xff08;N*N&#xff09;的格子地图上&#xff0c;宝宝和妈妈抽签决定各自 的位置&#xff0c;地图上每个格子有不同的Q糖果数量&#xff0c;部分格子有障碍物。 游戏规则Q是妈妈必须在最短的时间&a…

我独自升级崛起账号注册 我独自升级怎么注册账号

近期&#xff0c;《我独自升级》这部动画凭借爆棚的人气&#xff0c;在各大平台上掀起了一阵观看热潮&#xff0c;其影响力不容小觑。借此时机&#xff0c;韩国游戏巨头网石集团敏捷响应&#xff0c;顺势推出了同名游戏《我独自升级&#xff1a;ARISE》&#xff0c;为粉丝们搭建…

淘宝/天猫商品描述API(taobao.item_get_desc)返回值详解

淘宝/天猫的商品描述API&#xff08;taobao.item_get_desc&#xff09;允许开发者获取指定商品的详细描述信息。这对于需要进行商品数据分析、构建商品详情页面或进行其他与商品相关的应用开发非常有用。下面&#xff0c;我们将详细解析这个API的返回值。 一、API概述 taobao.…

接收区块链的CCF会议--NDSS 2025 截止7.10 附录用率

会议名称&#xff1a;Network and Distributed System Security Symposium (NDSS) CCF等级&#xff1a;CCF A类学术会议 类别&#xff1a;网络与信息安全 录用率&#xff1a;2024年接收率19.5% Submissions are solicited in, but not limited to, the following areas: Ant…