ES分页搜索及深分页deep paging问题介绍

ES在作为数据库查询时,少不了使用ES的分页功能。由于ES是一个分布式的文档存储系统,生产环境中,通常使用的是ES集群对应用提供搜索服务,在集群中,一个索引的数据会被分布在不同的shard上,而不同的分片又会被分布在不同的节点上,搜索某一个索引中的数据时,如果涉及到分页操作,ES就会将不同节点上被搜索的索引对应的数据取出来,作为一个全局的结果集,然后对这个全局结果集使用相关度分数排序,并按照分页的参数取出特定页码上的数据,最后返回。下面将介绍ES分页搜索的用法及分页中必须考虑到的深分页问题。

温馨提示:本博客已经发布小程序,可在微信小程序中搜索”百变码农”,手机上也能看!

1、ElasticSearch如何分页
(1)ES的分页语法
GET /index_name/indexType/_search?size=N
GET /index_name/indexType/_search?size=N&from=pageNo

参数解释:

N:表示的是每页查询的数据条数

pageNo:表示的是从第几条数据开始查,默认是从0开始的

(2)分页示例

查询student_index索引下studentType中第2页的数据,每页查询2条数据:

GET /student_index/studentType/_seach?from=2&size=2

返回结果:

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 6,  // 该索引中的数据总数
    "max_score": 1,
    "hits": [  // 第二页返回的数据列表
    {
      "_index": "student_index",
      "_type": "studentInfo",
      "_id": "4",
      "_score": 1,
      "_source": {   // 具体字段信息
        "name": "xiaochen", 
        "age": 22,
        "gender": "female"
      }
    },
    {
      "_index": "student_index",
      "_type": "studentInfo",
      "_id": "6",
      "_score": 1,
      "_source": {
        "name": "xiaohe",
        "age": 19,
        "gender": "female"
      }
    }
  ]
 }
}

注意:上述查询出来的结果中,包含了每页需要的数据以及总条数。在做分页的时候,可以查询一次,然后直接返回总数及分页之后的数据列表。而不需要单独去查询总数量,这个和MySQL中的实现方式有所不同。MySQL中,分页时需要查询分页的数据列表,还需要单独查询总数,需要操作两次数据库。

2、ElasticSearch的深分页问题
(1)ES的分页搜索步骤

在介绍ES的深分页问题之前,首先介绍一下ES分页搜索的基本步骤,大体可以分为如下的几步:

a、应用客户端发出搜索请求;

 

b、请求发送到某一个协调节点coordinate node上,然后由协调节点将请求解析出来通过路由,发送到其他索引所在的节点;

 

c、每个节点按照分页的参数返回指定条数的数据。例如:如果查询第一页,每页查询10条,则每个节点都会将当前节点上的前10条返回给协调节点;如果查询第二页,则每个节点都会将该节点上的前20条数据返回协调节点。最后由协调节点在内存中将返回的数据汇总,然后排序;

 

d、在排完序的列表中按照查询参数(页码,每页大小)从结果集中截取指定的列表,然后将数据列表返回;

比如:① 上述查询第一页10条数据,如果有5个分片,分布在5个节点上,则客户端查询第一页的时候,会将每个节点上的10条数据都返回给协调节点,协调节点上总共会收到50条数据,然后将这50条数据在协调节点上排序,再取出前10条返回;

② 如果查询第二页10条,则每个节点都会将前20条数据返回,然后协调节点上就会收到100条数据,然后将该100条数据排序,并取第10条到第20条数据返回给客户端。

(2)翻页时的问题

随着页码增加,协调节点上收到的数据量会快速增加,排序的数据会越来越多。当达到一定的数量时,会耗费特别多的内存(临时保存各节点返回的数据)和CPU(对各个节点返回的数据进行全局汇总和排序);甚至会导致ES节点内存溢出(OOM),节点挂掉。

(3)深分页的解决办法

a、业务上做限制

比如:业务上限制,如果一页搜索10条数据,最多只能搜索到前9000页,再往后不支持。该方案可以有效避免深分页,但是需要根据业务来确定是否可行。

b、使用ES提供的scroll机制

ES针对深分页提供了一种类似于关系型数据库中的游标的机制,搜索的过程分为初始化快照结果集和遍历结果集两步:

scroll机制原理:

① 第一次搜索完成之后,将所有复合条件的搜索结果缓存起来,类似于对结果集做了一个快照;

② 在需要返回数据时,从该快照中按照scroll返回数据;在scroll快照生成之后,在快照有效期范围内,对于该索引的增删改都不会影响快照的结果。

提升性能的关键点:在快照期间,继续翻页,不需要每次都去执行全局排序的操作,所以提升了性能;

c、使用ES提供的search_after机制

ES分页时使用scroll机制可以解决深分页问题,但是对于实时性要求不高的场景可以使用该机制,但是如果对于增删改操作之后,需要立刻反应在结果集中,该方式不是很适合。这时可以使用ES5.x之后提供的另外一个机制,即:search_after机制。

search_after机制的原理:

① 首先为每个文档生成一个全局唯一的标识id;

② 根据上一页的最后一条结果来确定下一页数据的位置,在搜索下一页时,需要将上一页的最后一条数据的唯一标识id带着。

该机制的优点:对于数据的增删改可以快速反应在搜索结果中,不会存在着太大的延迟;

至此,ES的分页用法及分页过程中的深分页问题及解决方案介绍完毕。本篇文章中理论描述总结较多,下篇文章将会通过实际示例来介绍ES深分页的解决方案。欢迎转发该文章!

温馨提示:如果小程序端代码显示混乱,是因为移动端兼容性导致,可移步至PC端站点查看!

文章属于原创,如果转发请标注文章来源:个人小站【www.jinnianshizhunian.vip

另外提供一些优秀的Java架构师及IT开发视频,书籍资料。无需注册,无需登录即可下载,免费下载地址:https://www.592xuexi.com