一、ES支持的三种分页查询方式

  • From + Size 查询
  • Scroll 遍历查询
  • Search After 查询


说明:
官方已经不再推荐采用Scroll API进行深度分页。如果遇到超过10000的深度分页,推荐采用search_after + PIT。

官方文档地址

二、分布式系统中的深度分页问题

为什么分布式存储系统中对深度分页支持都不怎么友好呢?

首先我们看一下分布式存储系统中分页查询的过程。

假设在一个有 4 个主分片的索引中搜索,每页返回10条记录。

当我们请求结果的第1页(结果从 1 到 10 ),每一个分片产生前 10 的结果,并且返回给 协调节点 ,协调节点对 40 个结果排序得到全部结果的前 10 个。

当我们请求第 99 页(结果从 990 到 1000),需要从每个分片中获取满足查询条件的前1000个结果,返回给协调节点, 然后协调节点对全部 4000 个结果排序,获取前10个记录。

当请求第10000页,每页10条记录,则需要先从每个分片中获取满足查询条件的前100010个结果,返回给协调节点。然后协调节点需要对全部(100010 * 分片数4)的结果进行排序,然后返回前10个记录。

可以看到,在分布式系统中,对结果排序的成本随分页的深度成指数上升。
这就是 web 搜索引擎对任何查询都不要返回超过 10000 个结果的原因。

三、From + Size 查询

1、准备数据

PUT user_index
{"mappings": {"properties": {"id":  {"type": "integer"},"name":   {"type": "keyword"}}}
}POST user_index/_bulk
{ "create":  {  "_id": "1" }}
{ "id":1,"name":"老万"}
{ "create":  {  "_id": "2" }}
{ "id":2,"name":"老王"}
{ "create":  {  "_id": "3" }}
{ "id":3,"name":"老刘"}
{ "create":  {  "_id": "4" }}
{ "id":4,"name":"小明"}
{ "create":  {  "_id": "5" }}
{ "id":5,"name":"小红"}

2、查询演示

无条件查询

POST user_index/_search

默认返回前10个匹配的匹配项。其中:
from:未指定,默认值是 0,注意不是1,代表当前页返回数据的起始值。
size:未指定,默认值是 10,代表当前页返回数据的条数。

指定from+size查询

POST user_index/_search
{"from": 0, "size": 10,"query": {"match_all": {}},"sort": [{"id": "asc"}    ]
}

3、max_result_window

es 默认采用的分页方式是 from+ size 的形式,在深度分页的情况下,这种使用方式效率是非常低的。
比如from = 5000, size=10, es 需要在各个分片上匹配排序并得到5000*10条有效数据,然后在结果集中取最后10条
数据返回,这种方式类似于mongo的 skip + size。

除了效率上的问题,还有一个无法解决的问题是,es 目前支持最大的 skip 值是 max_result_window ,默认为 10000
也就是当 from + size > max_result_window 时,es 将返回错误。

POST user_index/_search
{"from": 10000, "size": 10,"query": {"match_all": {}},"sort": [{"id": "asc"}    ]
}

这是ElasticSearch最简单的分页查询,但以上命令是会报错的。

报错信息,指window默认是10000。

"root_cause": [{"type": "illegal_argument_exception","reason": "Result window is too large, from + size must be less than or equal to: [10000] but was [10001]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level setting."}],"type": "search_phase_execution_exception",

怎么解决这个问题,首先能想到的就是调大这个window。

PUT user_index/_settings
{ "index" : { "max_result_window" : 20000}
}

然后这种方式只能暂时解决问题,当es 的使用越来越多,数据量越来越大,深度分页的场景越来越复杂时,如何解决这种问题呢?

官方建议:
避免过度使用 from 和 size 来分页或一次请求太多结果。

不推荐使用 from + size 做深度分页查询的核心原因:

  • 搜索请求通常跨越多个分片,每个分片必须将其请求的命中内容以及任何先前页面的命中内容加载到内存中。
  • 对于翻页较深的页面或大量结果,这些操作会显著增加内存和 CPU 使用率,从而导致性能下降或节点故障。

四、Search After 查询

search_after 参数使用上一页中的一组排序值来检索下一页的数据。
使用 search_after 需要具有相同查询和排序值的多个搜索请求。 如果在这些请求之间发生刷新,结果的顺序可能会发生变化,从而导致跨页面的结果不一致。 为防止出现这种情况,您可以创建一个时间点 (PIT) 以保留搜索中的当前索引状态。

时间点 Point In Time(PIT)保障搜索过程中保留特定事件点的索引状态。

注意⚠️:
es 给出了 search_after 的方式,这是在 >= 5.0 版本才提供的功能。
Point In Time(PIT)是 Elasticsearch 7.10 版本之后才有的新特性。

PIT的本质:存储索引数据状态的轻量级视图。

如下示例能很好的解读 PIT 视图的内涵。

#1、给索引user_index创建pit
POST /user_index/_pit?keep_alive=5m#2、统计当前记录数 5
POST /user_index/_count#3、根据pit统计当前记录数 5
GET /_search
{"query": {"match_all": {}},"pit": {"id":  "i6-xAwEKdXNlcl9pbmRleBZYTXdtSFRHeVJrZVhCby1OTjlHMS1nABZ0TEpMcVRuNFRxaWI4cXFTVERhOHR3AAAAAAAAIODBFmdBWEd2UmFVVGllZldNdnhPZDJmX0EBFlhNd21IVEd5UmtlWEJvLU5OOUcxLWcAAA==", "keep_alive": "5m"},"sort": [{"id": "asc"}    ]
}
#4、插入一条数据
POST user_index/_bulk
{ "create":  {  "_id": "6" }}
{ "id":6,"name":"老李"}#5、数据总量 6
POST /user_index/_count#6、根据pit统计数据总量还是 5 ,说明是根据时间点的视图进行统计。
GET /_search
{"query": {"match_all": {}},"pit": {"id":  "i6-xAwEKdXNlcl9pbmRleBZYTXdtSFRHeVJrZVhCby1OTjlHMS1nABZ0TEpMcVRuNFRxaWI4cXFTVERhOHR3AAAAAAAAIODBFmdBWEd2UmFVVGllZldNdnhPZDJmX0EBFlhNd21IVEd5UmtlWEJvLU5OOUcxLWcAAA==", "keep_alive": "5m"},"sort": [{"id": "asc"}    ]
}

有了 PIT,search_after 的后续查询都是基于 PIT 视图进行,能有效保障数据的一致性。

search_after 分页查询可以简单概括为如下几个步骤。

1、获取索引的pit

POST /user_index/_pit?keep_alive=5m

2、根据pit首次查询

说明:根据pit查询的时候,不用指定索引名称。

GET /_search
{"query": {"match_all": {}},"pit": {"id":  "i6-xAwEKdXNlcl9pbmRleBZYTXdtSFRHeVJrZVhCby1OTjlHMS1nABZ0TEpMcVRuNFRxaWI4cXFTVERhOHR3AAAAAAAAIODBFmdBWEd2UmFVVGllZldNdnhPZDJmX0EBFlhNd21IVEd5UmtlWEJvLU5OOUcxLWcAAA==", "keep_alive": "1m"},"sort": [{"id": "asc"}    ]
}

查询结果:返回的sort值为2.

hits" : [{"_index" : "user_index","_type" : "_doc","_id" : "2","_score" : null,"_source" : {"id" : 2,"name" : "老王"},"sort" : [2]}]

3、根据search_after和pit进行翻页查询

说明:
search_after指定为上一次查询返回的sort值。
要获得下一页结果,请使用最后一次命中的排序值(包括 tiebreaker)作为 search_after 参数重新运行先前的搜索。 如果使用 PIT,请在 pit.id 参数中使用最新的 PIT ID。 搜索的查询和排序参数必须保持不变。 如果提供,则 from 参数必须为 0(默认值)或 -1。

GET /_search
{"size": 1, "query": {"match_all": {}},"pit": {"id":  "i6-xAwEKdXNlcl9pbmRleBZYTXdtSFRHeVJrZVhCby1OTjlHMS1nABZ0TEpMcVRuNFRxaWI4cXFTVERhOHR3AAAAAAAAIOJ7FmdBWEd2UmFVVGllZldNdnhPZDJmX0EBFlhNd21IVEd5UmtlWEJvLU5OOUcxLWcAAA==", "keep_alive": "5m"},"sort": [{"id": "asc"}    ],"search_after": [                                2]
}

优缺点分析

search_after 查询仅支持向后翻页。
不严格受制于 max_result_window,可以无限制往后翻页。
单次请求值不能超过 max_result_window;但总翻页结果集可以超过。

思考

ES深度分页查询详解相关推荐

  1. ES中SQL查询详解

    一.Elasticsearch SQL简介 Elasticsearch SQL 是一个 X-Pack 组件,它允许对 Elasticsearch 实时执行类似 SQL 的查询.无论是使用 REST 接 ...

  2. 查询参数HQL实现普通查询及分页查询详解

    题记:写这篇博客要主是加深自己对查询参数的认识和总结实现算法时的一些验经和训教,如果有错误请指出,万分感谢. HQL查询: Criteria查询对查询条件行进了面向对象装封,符合程编员人的思维式方,不 ...

  3. Java分页查询详解

    分页sql select * from 表名 limit 0,20;//第一页 select * from 表名 limit 20,20;//第二页 select * from 表名 limit 40 ...

  4. mybatis-plus分页查询详解

    文章目录 一.官方文档 二.内置的分页方法 1.内置方法 2.selectPage单元测试 3.PaginationInnerInterceptor分页插件配置 三.分页原理分析 四.自定义分页方法 ...

  5. MySql深分页问题详解

    MySql深分页问题详解 1. 问题描述 2. 问题分析 3. 验证测试 3.1 创建两个表 3.2 创建两个函数 3.3 编写存储过程 3.4 编写存储过程 3.5 创建索引 3.6 验证测试 4. ...

  6. ElasticSearch 全文搜索引擎的查询详解①(Ubuntu版 v6.6.2)

    ElasticSearch 全文搜索引擎的查询详解①(Ubuntu版 v6.6.2) 1. 前提 2. 轻量搜索 2.1 单条件查询 2.2 多条件查询 2.3 不指定属性查询(查询所有文档属性)-- ...

  7. Elasticsearch实战——function_score 查询详解

    Elasticsearch实战--function_score 查询详解 文章目录 Elasticsearch实战--function_score 查询详解 1. function_score简介 2 ...

  8. 深度学习优化函数详解(5)-- Nesterov accelerated gradient (NAG) 优化算法

    深度学习优化函数详解系列目录 深度学习优化函数详解(0)– 线性回归问题 深度学习优化函数详解(1)– Gradient Descent 梯度下降法 深度学习优化函数详解(2)– SGD 随机梯度下降 ...

  9. 【深度学习】详解Resampling和softmax模型集成

    [深度学习]详解Resampling和softmax模型集成 文章目录 1 图像重采样1.1 次级采样(sub-sampling)1.2 高斯金字塔(Gaussian pyramids)1.3 上采样 ...

最新文章

  1. ubuntu下移植QT基本流程
  2. pycharm连接远程mysql_CentOS7安装mysql以及使用pycharm远程连接mysql时遇到的问题
  3. azdb文件怎么打开_AZDBExplorerSvcs.dll
  4. leetcode132. 分割回文串 II
  5. PyTorch自定义CUDA算子教程与运行时间分析
  6. java抽象类可以new_java的抽象类,接口,普通类是否可以直接NEW,并且复写方法?
  7. Centos在VMware虚拟机上的网络配置一记
  8. 初学者之如何快速获取微信小程序源码
  9. python源码剖析新版_Python 源码剖析之基础知识
  10. 庞皓计量经济学第四版_庞皓计量经济学第4版笔记和课后答案
  11. 如何从员工晋升为合格管理者?
  12. office彻底卸载工具
  13. SQL经典50查询语句案例_3(查询所有同学的学号、姓名、选课数、总成绩)
  14. 在windows 7上是否可以运行win 10的应用
  15. 实战一:给定一段音频,请提取12维MFCC特征,阅读代码预加重、分帧、加窗部分,完善作业代码中fbank和mfcc部分,并给出最终的Fbank和MFCC特征,用默认的配置参数,无需进行修改
  16. 幽灵蛛(pholcus)(三)--strings学习资料
  17. XPO 的三篇介绍文章。
  18. android字符串+数字变量方法之%1$s、%1$d的用法
  19. 银河麒麟禁止抓屏printScreen
  20. 布法罗大学计算机硕士学费,纽约布法罗大学学费是多少

热门文章

  1. iOS - ECC椭圆曲线、ECDSA签名验签和ECIES加解密本文来源
  2. 百度浏览器奔跑的小熊
  3. 基于HTML5 SVG可互动的3D标签云jQuery插件
  4. oa人员导入模板_OA人员管理系统项目
  5. 【100%通过率】华为OD机试真题 C++ 实现【最长回文字符串】【2023 Q1 | 100分】
  6. 浅析医药工业类洁净厂房电气消防设计与应用
  7. 文本语音朗读软件 c#程序
  8. 自建dayz服务器,【图片】关于自建服务器的支援贴【dayz吧】_百度贴吧
  9. Bliface借区块链定义视频网站3.0
  10. 在VScode中创建Java项目