ElasticSearch中的深度分页问题

在使用 ElasticSearch 进行搜索时,很多小伙伴会遇到“深度分页”问题。当需要获取大量的分页数据时,查询性能会急剧下降,甚至导致集群负载过高。这篇文章将深入剖析 ElasticSearch 深度分页的成因、危害,并提供一些常用的优化方案。


一、什么是深度分页?

深度分页的定义

在 ElasticSearch 中,我们可以通过 from 和 size 参数进行分页控制:

GET /my_index/_search
{
  "from": 0,
  "size": 10
}
  • from:跳过的记录数
  • size:返回的记录数

例如,from=1000size=10 表示跳过前 1000 条记录,从第 1001 条开始获取 10 条记录。

深度分页就是:

        当 from 参数的值很大时(如 from=10000 以上),ElasticSearch 需要跳过大量数据来获取指定页的内容,这种情况称为深度分页

深度分页的危害

在 ElasticSearch 中,数据是存储在多个分片(Shard)上的,每个分片本质上是一个独立的 Lucene 索引。分页查询会在每个分片上独立执行查询,然后将结果合并和排序。理解深度分页的危害需要从 ElasticSearch 的分布式架构和分片查询流程入手。

分片查询的工作原理

  • 分布式存储
    当一个索引被分配多个分片(例如,5 个分片)时,数据会被均匀分布到不同分片上。每个分片可以分布在不同的节点上。

  • 并行查询
    当进行查询时,ElasticSearch 会将查询请求发送到所有分片,每个分片独立执行查询并返回结果。

  • 结果合并
    收集每个分片返回的结果后,ElasticSearch 在协调节点上进行全局排序和合并,最终返回指定的 fromsize 范围内的结果。

深度分页时分片的执行流程

GET /my_index/_search
{
  "from": 10000,
  "size": 10
}
  • 每个分片分别查询
    每个分片都会独立查询出至少 from + size = 10010 条数据。

  • 本地排序
    每个分片对这 10010 条数据进行排序。

  • 返回数据
    每个分片返回排序后的前 10010 条数据到协调节点。

  • 全局排序与裁剪
    协调节点将所有分片的结果合并,然后进行全局排序,并在这些结果中取第 10000 到 10010 条数据,最终返回给用户。

深度分页的危害剖析

我们分析一下上述过程,这会带来一些什么危害呢

内存消耗急剧增加
  • 分片内存开销
    每个分片需要在内存中加载 from + size 条数据(例如 10010 条)。如果分片较多且数据量庞大,这会导致每个分片消耗大量内存。

  • 协调节点内存开销
    协调节点需要收集所有分片返回的大量数据,并在内存中进行全局排序。这可能导致内存溢出(OOM)。

磁盘和 CPU 负载增加
  • 分片读取开销
    为了跳过大量数据,每个分片必须从磁盘中读取大量文档,增加了 I/O 开销。

  • 排序计算开销
    每个分片需要对大量数据进行排序,然后协调节点需要再进行全局排序,增加了 CPU 计算负担。

查询延迟显著增加
  • 线性增长的延迟
    随着 from 值增大,每个分片需要加载和处理的数据量增多,导致查询延迟线性增加。例如,从第 10000 条开始获取数据的延迟会远高于从第 100 条开始获取数据。
集群稳定性风险
  • 节点压力
    大量内存和 CPU 的消耗会增加分片所在节点的负载,严重时可能导致节点性能下降甚至崩溃。

  • 全局影响
    单个深度分页查询可能拖垮整个集群,影响其他正常查询的性能,降低服务可用性。

二、ES中的深度分页方式

如果使用from、size来实现ES的分页查询,我们将会面临深度分页问题,但是我们又想进行深度分页,ES如何解决呢,我们来看下面两种分页方式

Scroll滚动查询

如何解决深度分页

在 ElasticSearch 中,使用传统的分页方式(from + size)进行深度分页会导致性能急剧下降。比如,当你查询第 10000 页的数据时,from 设置为 999900,这意味着 ElasticSearch 需要扫描前 999900 条记录并将其加载到内存中,再丢弃这些记录,只保留最后 100 条。这种操作不仅浪费大量内存,而且延迟非常高。

Scroll 滚动查询通过引入游标机制数据快照,有效地避免了这种大规模跳过数据带来的性能问题。

举个例子:

假设你需要从一张 100 万条记录的大表中导出所有数据。如果使用传统分页方法(from + size),当你查询第 10000 页时,ElasticSearch 需要先扫描和加载前 999900 条数据,再丢弃它们,仅返回你需要的 100 条数据。这就像每次找第 10000 本书,都要从第 1 本开始数到第 10000 本,既费时又耗力。

而使用 Scroll 滚动查询 就像拥有一个记忆书签,每次查询都会在你停下的地方做个标记,下次直接从这个标记处继续,不需要重新扫描前面的数据。它通过创建一个数据快照(Snapshot),固定查询时的数据状态,并维护一个滚动上下文,保证每次返回数据的同时保存位置,避免重复扫描。

使用方式

初始化滚动查询

GET /my_index/_search?scroll=1m
{
  "size": 100,
  "query": { "match_all": {} }
}
  • scroll=1m:滚动上下文有效期为 1 分钟。
  • size=100:每次返回 100 条记录。

响应如下:

获取下一批数据

GET /_search/scroll
{
  "scroll": "1m",
  "scroll_id": "DnF1ZXJ5VGhlbkZldGNoAwAAAAAAA..."
}

响应也是和上面的响应结构相同,只是返回的scroll_id不同 

清理滚动上下文

完成查询后,及时清理滚动上下文以释放内存:

DELETE /_search/scroll
{
  "scroll_id": "DnF1ZXJ5VGhlbkZldGNoAwAAAAAAA..."
}

优缺点

  • 优点

    • 避免跳过大量数据,性能稳定。
    • 保持数据一致性,适合处理大量数据的场景。
    • 不受 max_result_window 限制,可以获取超过 10000 条的数据。
  • 缺点

    • 内存占用:滚动上下文会占用内存,需要及时释放。
    • 只适用于静态数据:动态更新的数据在滚动过程中不会反映最新变化。
    • 只能向前遍历,不支持随机跳转。

使用场景

  • 数据导出:将索引中的数据批量导出到外部存储。
  • 日志分析:批量分析和处理日志数据。
  • 批处理任务:需要处理大量数据的离线任务,如数据迁移和备份。

滚动查询的工作原理

  1. 创建快照
    第一次查询时,ElasticSearch 为查询结果集创建一个快照,该快照保持数据的一致性,即使在滚动查询过程中有新的数据写入索引,查询结果依然基于快照的数据集,不受新数据影响。

  2. 分批读取
    每次滚动查询返回指定数量的数据(例如 100 条),并生成一个 _scroll_id,作为指向下一批数据的游标。ElasticSearch 会根据这个游标继续从上次结束的位置读取下一批数据,而无需重新扫描前面的数据。

  3. 游标推进
    每次查询会返回新的 _scroll_id,用于后续请求。你只需提供这个 _scroll_id,ElasticSearch 就能知道从哪里继续查找,而不必跳过大量记录。

search_after

如何解决深度分页

search_after 通过使用上一页最后一条记录的排序值来定位下一页的起始点,避免了传统分页需要跳过大量数据的问题。这种方法在需要实时展示大量数据时特别有效。

举个例子:
想象你在一个电子商城里查看商品,商品按价格升序排列。传统的分页就像数到第 10000 个商品,跳过前 9999 个才能展示出来。而 search_after 则是记住第 100 条商品的价格和编号,下次从这个位置直接继续展示第 101 条商品,不用重新扫描前面的商品。

使用方式

第一次查询

GET /my_index/_search
{
  "size": 10,
  "sort": [
    { "price": "asc" },
    { "_id": "asc" }
  ]
}

后续查询

使用上一页最后一条记录的排序值作为 search_after 参数,获取下一页数据。

GET /my_index/_search
{
  "size": 10,
  "sort": [
    { "price": "asc" },
    { "_id": "asc" }
  ],
  "search_after": [ 199.99, "product_12345" ]
}

优缺点

  • 优点

    • 高效分页:避免跳过大量数据,查询性能稳定。
    • 内存占用小:无状态查询,不需要维护上下文。
    • 实时性强:适合动态数据的实时分页。
  • 缺点

    • 只能向前分页:无法跳转到任意页,只能按顺序向前获取数据。
    • 排序要求:必须有唯一的排序字段组合,确保结果的唯一性。

使用场景

  • 实时数据展示:新闻、商品、用户动态等实时数据的分页查询。
  • 日志检索:按时间戳顺序查询和分析日志。
  • 动态数据分析:需要连续获取新数据并按特定顺序排列的场景。

search_after的工作原理

  1. 排序值标记位置
    每次查询结果都会包含排序字段的值(例如 price_id)。search_after 通过这些值标记当前位置,下次查询时从这个位置继续。

  2. 避免跳过数据
    不使用 from 来跳过记录,而是精确地从上次查询的最后位置开始读取。

  3. 无状态查询
    每次查询都是独立的,不需要在服务器上维持上下文,节省内存。

PIT与search_after结合

为什么 search_after 需要配合 PIT(Point in Time)?

直接使用 search_after 进行分页确实可行,但在某些场景下,它有显著的局限性,而引入 PIT(Point in Time) 可以有效解决这些问题。下面我们来详细解释为什么需要结合 PIT,以及它带来的优势。

直接使用 search_after 的问题

  1. 数据不一致性
    如果在分页查询过程中,索引中的数据发生了变化(例如新增、更新或删除文档),每次查询返回的结果可能会受到这些变化的影响,导致数据不一致。

    示例场景

    • 第一次查询:你获取了排序后第 100 条记录,排序字段的值是 timestamp: 2024-06-16T10:00:00
    • 在第二次查询之前:有一条新的记录被插入,排序值正好在你上次获取的记录之前。
    • 第二次查询:当你使用 search_after 进行下一页查询时,结果集可能会跳过或重复某些记录,导致分页不连续。
  2. 并发查询干扰
    当多个查询或数据写入操作并发进行时,直接使用 search_after 的分页请求很容易受到其他操作的干扰,导致分页结果不可预测。

  3. 无法保证长时间分页
    在长时间的分页操作中,数据的变化可能越来越多,导致分页的连续性和完整性无法保证。

PIT(Point in Time)如何解决这些问题

PIT 通过在查询开始时创建一个固定数据视图来解决上述问题。具体来说:

  1. 数据一致性保证

    • 固定视图:PIT 创建时会固定一个数据视图,即使索引中新增、更新或删除了数据,PIT 下的查询结果集不会受到这些变化的影响。
    • 这意味着所有的分页请求都会基于创建 PIT 时的数据快照进行,保证分页结果的连续性和一致性。

    举例说明
    想象你正在看一张快照照片,这张照片捕捉了某一瞬间的所有信息。即使在现实中事物发生了变化,照片里的信息依然保持不变。PIT 就像这样一张数据的快照,让你在分页时始终看到快照时刻的结果。

  2. 避免并发干扰

    • PIT 创建的数据视图是独立的,不会受到其他并发查询或写入操作的干扰。因此,即使在高并发场景下,你的分页查询也能保持稳定。
  3. 长时间分页安全

    • 通过设置合适的 keep_alive 时间(如 1m5m),PIT 可以保持长时间有效,支持长时间的分页操作而不会因为数据变化导致分页结果混乱。

PIT+search_after使用方式

1.创建 PIT 视图
创建一个固定的数据视图,并设置有效期(如 1 分钟):

POST /my_index/_search?keep_alive=1m
{
  "size": 10,
  "sort": [
    { "timestamp": "asc" },
    { "_id": "asc" }
  ]
}

返回的 pit_id 用于后续分页请求。

2.分页查询
使用 pit_idsearch_after 进行分页:

POST /_search
{
  "size": 10,
  "pit": {
    "id": "46ToAwMDaWR...ZmZjZTg",
    "keep_alive": "1m"
  },
  "sort": [
    { "timestamp": "asc" },
    { "_id": "asc" }
  ],
  "search_after": [ "2024-06-16T10:00:00", "record_123" ]
}

3.关闭 PIT 视图
分页完成后,关闭 PIT 释放资源:

DELETE /_pit
{
  "id": "46ToAwMDaWR...ZmZjZTg"
}

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

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

相关文章

Eureka学习笔记-服务端

Eureka学习笔记 服务端 模块设计 Resources :这部分对外暴露了一系列的 Restful 接口。Eureka Client 的注册、心跳、获取服务列表等操作都需要调用这些接口。另外,其他的 Server 在同步 Registry 时也需要调用这些接口。Controller :这里提…

快速上手:利用 FFmpeg 合并音频文件的实用教程

FFmpeg 是一个强大的多媒体处理工具,能够轻松地对音频、视频进行编辑和转换。本文将介绍如何使用 FFmpeg 来合并(拼接)多个音频文件为一个单一文件。无论您是想要创建播客、音乐混音还是其他任何形式的音频项目,这都是一个非常实用…

在 CUDA C/C++ 中使用共享內存

文章结尾有最新热度的文章,感兴趣的可以去看看。 本文是经过严格查阅相关权威文献和资料,形成的专业的可靠的内容。全文数据都有据可依,可回溯。特别申明:数据和资料已获得授权。本文内容,不涉及任何偏颇观点,用中立态度客观事实描述事情本身 文章有点长(4700字),期望您…

Linux文件属性 --- 硬链接、所有者、所属组

三、硬链接数 1.目录 使用“ll”命令查看,在文件权限的后面有一列数字,这是文件的硬链接数。 对于目录,硬链接的数量是它具有的直接子目录的数量加上其父目录和自身。 下图的“qwe”目录就是“abc”目录的直接子目录。 2.文件 对于文件可…

RAG开发中,如何用Milvus 2.5 BM25算法实现混合搜索

01. 背景 混合搜索(Hybrid Search)作为RAG应用中Retrieve重要的一环,通常指的是将向量搜索与基于关键词的搜索(全文检索)相结合,并使用RRF算法合并、并重排两种不同检索的结果,最终来提高数据的召回率。全文检索与语义…

SpringAI人工智能开发框架002---SpringAI项目搭建_依赖导入_maven仓库引入_接口中转

然后看依赖.可以看到 看一下官网springAI的最新版本看到是0.8.1 这里我们用1.0.0这个最新的,快照版 1.0.0-snapshot 然后再来看,需要导入这个 spring-ai-openai-spring-boot-starter 这个依赖是openai的.

机器学习之偏差

机器学习中的偏差(Bias)是指模型的预测值与真实值之间的系统性误差,或者说模型无法准确捕捉数据中复杂模式的能力。偏差通常与模型的假设或学习能力有关,过高的偏差会导致模型的性能不佳,表现为欠拟合。 偏差的来源 模…

(css)鼠标移入或点击改变背景图片

(css)鼠标移入或点击改变背景图片 html <div class"mapTip"><divv-for"(item, index) of legendList":key"index"class"mapTipOne":class"{ active: change index }"click"legendHandle(item, index)"…

二、windows环境下vscode使用wsl教程

本篇文件介绍了在windows系统使用vscode如何连接使用wsl&#xff0c;方便wsl在vscode进行开发。 1、插件安装 双击桌面vscode&#xff0c;按快捷键CtrlShiftX打开插件市场&#xff0c;搜索【WSL】点击安装即可。 2、开启WSL的linux子系统 点击左下方图标【Open a Remote Win…

不同数据中心间海量数据的安全加密传输方案

安当TDE&#xff08;透明数据加密&#xff09;为实现不同数据中心间异地海量数据安全传输提供了一套高效且可靠的解决方案。以下是该解决方案的具体内容&#xff1a; 一、透明加密机制 安当TDE的核心在于其透明加密机制。在数据传输前&#xff0c;TDE能够自动对文件进行加密处…

前后端分离的项目使用nginx 解决 Invalid CORS request

我是这样打算的&#xff0c;前端用nginx代理&#xff0c;使用80 转443 端口走https 前端的地址就是http://yumbo.top 或https://yumbo.top 后端服务地址是&#xff1a;http://yumbo.top:8081 下面是我的完整配置&#xff0c;功能是正常的&#xff0c;加了注释 user nginx; …

边缘智能创新应用大赛获奖作品系列一:智能边缘计算✖软硬件一体化,开启全场景效能革命新征程

边缘智能技术快速迭代&#xff0c;并与行业深度融合。它正重塑产业格局&#xff0c;催生新产品、新体验&#xff0c;带动终端需求增长。为促进边缘智能技术的进步与发展&#xff0c;拓展开发者的思路与能力&#xff0c;挖掘边缘智能应用的创新与潜能&#xff0c;高通技术公司联…

【自动化】Python SeleniumUtil 工具 开启开发者模式 自动安装油猴用户脚本等

【自动化】Python SeleniumUtil 工具 【Python】使用Selenium 操作浏览器 自动化测试 记录-CSDN博客文章浏览阅读58次。文章浏览阅读42次。【附件】Selenium chromedriver 驱动及浏览器下载。【附件】Selenium chromedriver 驱动及浏览器下载-CSDN博客。3.安装Chrome浏览器驱动…

三、使用langchain搭建RAG:金融问答机器人--检索增强生成

经过前面2节数据准备后&#xff0c;现在来构建检索 加载向量数据库 from langchain.vectorstores import Chroma from langchain_huggingface import HuggingFaceEmbeddings import os# 定义 Embeddings embeddings HuggingFaceEmbeddings(model_name"m3e-base")#…

Springboot应用开发:工具类整理

目录 一、编写目的 二、映射工具类 2.1 依赖 2.2 代码 三、日期格式 3.1 依赖 3.2 代码 四、加密 4.1 代码 五、Http请求 5.1 依赖 5.2 代码 六、金额 6.1 代码 七、二维码 7.1 依赖 7.2 代码 八、坐标转换 8.1 代码 九、树结构 9.1 代码 9.1.1 节点 9.1…

【第一节】Git的简介和安装

目录 一、git的介绍 二、git 的安装 2.1 Linux 平台安装 2.2 源码安装 2.3 Windows 平台安装 2.4 Mac 平台安装 2.5 Git 配置 2.5.1 配置文件 2.5.2 用户信息配置 2.5.3 文本编辑器配置 2.5.4 差异分析工具配置 2.5.5 查看配置信息 一、git的介绍 Git 是一种开源的…

数据库和SQL的基本概念

目录 定义数据分类非结构化数据&#xff1a;半结构化数据 :​ 结构化数据 : SQL(Structured Query Language)概念分类 关系模型常用的概念数据库管理概念理解 定义 数据库(Database)是按照数据结构来组织、存储和管理数据的建立在计算机存储设备上的仓库。 数据库是长期储存在…

oneflow深度学习框架使用问题总结(Windows/Linux)

目录 1.简述 2.在Windows下使用Oneflow深度学习框架&#xff08;错误记录&#xff0c;谨慎&#xff0c;官方不支持&#xff0c;需要WSL&#xff09; 2.1安装Anaconda 2.1创建虚拟环境 2.2安装Pytorch 2.3安装Pycharm 2.4 安装Oneflow 3.在Linux下使用Oneflow深度学习框…

TypeScript 变量与常量

文章目录 一、变量声明(一)let 关键字(块级作用域)(二)var 关键字(函数级作用域,与 let 的区别)二、常量声明(一)const 关键字(不可重新赋值)一、变量声明 (一)let 关键字(块级作用域) 在 TypeScript 中,let 关键字用于声明变量,它所声明的变量具有块级作用…

【Go】-倒排索引的简单实现

目录 什么是倒排索引 定义 基本结构和原理 分词在倒排索引中的重要性 简单倒排索引的实现 接口定义 简单数据库的实现 倒排索引 正排索引 测试 总结 什么是倒排索引 定义 倒排索引&#xff08;Inverted Index&#xff09;是一种索引数据结构&#xff0c;它是文档检…