当前位置:   article > 正文

Elasticsearch三种分页方式_elasticsearch 分页

elasticsearch 分页

Elasticsearch中数据都存储在分片中,当执行搜索时每个分片独立搜索后,数据再经过整合返回。那么,如何实现分页查询呢?

按照一般的查询流程来说,如果我想查询前10条数据:

  1. 客户端请求发给某个节点
  2. 节点将请求转发到集群其他节点,各节点返回是否包含该请求信息,然后该节点再发送二次请求给具体包含该query倒排的节点上进行计算,查询每个分片上的前10条
  3. 结果返回给节点,整合数据,提取前10条
  4. 返回给请求客户端

那么当我想要查询第10条到第20条的数据该怎么办呢?这个时候就用到分页查询了。在ElasticSearch中实现分页查询的方式有一下三种方式。

一、深度分页(from+size)

es 默认采用的分页方式是 from+ size 的形式,原理很简单,当查询10-20条数据时,就在相应的各节点上直接查询前20条数据,然后截断前10条,只返回10-20的数据,最后在返回协调鞋垫,整合数据,返回10条数据。

  1. GET /student/student/_search
  2. {
  3. "query":{
  4. "match_all": {}
  5. },
  6. "from":5000,
  7. "size":10
  8. }

其中,from定义了目标数据的偏移值,size定义当前返回的事件数目(默认from为0,size为10),即所有的查询默认仅仅返回前10条数据。 

上面的查询意味着 es 需要在各个分片上匹配排序并得到5010条数据,由此可见:随着页数增加,深度分页将会使得效率非常低,因为我只需要查询size条数据,而es则需要执行from+size条数据然后处理后返回。

当size + from > 10000时,es查询失败,并且提示:

  1. Result window is too large, from + size must be less than or equal to: [10000] but was [10001]
  2. 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

二、快照查询(scroll)

在es中如果我们分页要请求大数据集或者一次请求要获取较大的数据集,scroll都是一个非常好的解决方案。

使用scroll滚动搜索,可以先搜索一批数据,然后下次再搜索一批数据,以此类推,直到搜索出全部的数据来。scroll搜索会在第一次搜索的时候,保存一个当时的视图快照,之后只会基于该旧的视图快照提供数据搜索,如果这个期间数据变更,是不会让用户看到的。每次发送scroll请求,我们还需要指定一个scroll参数,指定一个时间窗口,每次搜索请求只要在这个时间窗口内能完成就可以了。

一个滚屏搜索允许我们做一个初始阶段搜索并且持续批量从Elasticsearch里拉取结果直到没有结果剩下。这有点像传统数据库里的cursors(游标)。

滚屏搜索会及时制作快照。这个快照不会包含任何在初始阶段搜索请求后对index做的修改。它通过将旧的数据文件保存在手边,所以可以保护index的样子看起来像搜索开始时的样子。这样将使得我们无法得到用户最近的更新行为。

scroll的使用很简单。执行如下curl,每次请求两条。可以定制 scroll = 5m意味着该窗口过期时间为5分钟。

  1. GET /student/student/_search?scroll=5m
  2. {
  3. "query": {
  4. "match_all": {}
  5. },
  6. "size": 2
  7. }

返回:

  1. {
  2. "_scroll_id" : "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAC0YFmllUjV1QTIyU25XMHBTck1XNHpFWUEAAAAAAAAtGRZpZVI1dUEyMlNuVzBwU3JNVzR6RVlBAAAAAAAALRsWaWVSNXVBMjJTblcwcFNyTVc0ekVZQQAAAAAAAC0aFmllUjV1QTIyU25XMHBTck1XNHpFWUEAAAAAAAAtHBZpZVI1dUEyMlNuVzBwU3JNVzR6RVlB",
  3. "took" : 0,
  4. "timed_out" : false,
  5. "_shards" : {
  6. "total" : 5,
  7. "successful" : 5,
  8. "skipped" : 0,
  9. "failed" : 0
  10. },
  11. "hits" : {
  12. "total" : 6,
  13. "max_score" : 1.0,
  14. "hits" : [
  15. {
  16. "_index" : "student",
  17. "_type" : "student",
  18. "_id" : "5",
  19. "_score" : 1.0,
  20. "_source" : {
  21. "name" : "fucheng",
  22. "age" : 23,
  23. "class" : "2-3"
  24. }
  25. },
  26. {
  27. "_index" : "student",
  28. "_type" : "student",
  29. "_id" : "2",
  30. "_score" : 1.0,
  31. "_source" : {
  32. "name" : "xiaoming",
  33. "age" : 25,
  34. "class" : "2-1"
  35. }
  36. }
  37. ]
  38. }
  39. }

返回信息里有一个重点的参数scroll_id(base64编码),在后面的请求中我们都要带着这个 scroll_id 去请求。student这个索引中共有6条数据,id分别为 1, 2, 3, 4, 5, 6。当我们使用 scroll 查询第4次的时候,返回结果应该为空。这时我们就知道已经结果集已经匹配完了。继续执行3次结果如下三图所示:

  1. GET /_search/scroll
  2. {
  3. "scroll":"5m",
  4. "scroll_id":"DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAC0YFmllUjV1QTIyU25XMHBTck1XNHpFWUEAAAAAAAAtGRZpZVI1dUEyMlNuVzBwU3JNVzR6RVlBAAAAAAAALRsWaWVSNXVBMjJTblcwcFNyTVc0ekVZQQAAAAAAAC0aFmllUjV1QTIyU25XMHBTck1XNHpFWUEAAAAAAAAtHBZpZVI1dUEyMlNuVzBwU3JNVzR6RVlB"
  5. }

三、search_after

from + size的分页方式虽然是最灵活的分页方式,但是当分页深度达到一定程度将会产生深度分页的问题。scroll能够解决深度分页的问题,但是其无法实现实时查询,即当scroll_id生成后无法查询到之后数据的变更,因为其底层原理是生成数据的快照。这时 search_after应运而生。其是在es-5.X之后才提供的。

search_after 是一种假分页方式,根据上一页的最后一条数据来确定下一页的位置,同时在分页请求的过程中,如果有索引数据的增删改查,这些变更也会实时的反映到游标上。为了找到每一页最后一条数据,每个文档必须有一个全局唯一值,官方推荐使用 _uid 作为全局唯一值,但是只要能表示其唯一性就可以。

为了演示,我们需要给上文中的student索引增加一个uid字段表示其唯一性。

  1. GET /student/student/_search
  2. {
  3. "query":{
  4. "match_all": {}
  5. },
  6. "size":2,
  7. "sort":[
  8. {
  9. "uid": "desc"
  10. }
  11. ]
  12. }

 查询结果:

  1. {
  2. "took" : 1,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 5,
  6. "successful" : 5,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : 6,
  12. "max_score" : null,
  13. "hits" : [
  14. {
  15. "_index" : "student",
  16. "_type" : "student",
  17. "_id" : "6",
  18. "_score" : null,
  19. "_source" : {
  20. "uid" : 1006,
  21. "name" : "dehua",
  22. "age" : 27
  23. },
  24. "sort" : [
  25. 1006
  26. ]
  27. },
  28. {
  29. "_index" : "student",
  30. "_type" : "student",
  31. "_id" : "5",
  32. "_score" : null,
  33. "_source" : {
  34. "uid" : 1005,
  35. "name" : "fucheng",
  36. "age" : 23
  37. },
  38. "sort" : [
  39. 1005
  40. ]
  41. }
  42. ]
  43. }
  44. }

 下一次分页,需要将上述分页结果集的最后一条数据的值带上

  1. GET /student/student/_search
  2. {
  3. "query":{
  4. "match_all": {}
  5. },
  6. "size":2,
  7. "search_after":[1005],
  8. "sort":[
  9. {
  10. "uid": "desc"
  11. }
  12. ]
  13. }

 三种方式对对比:

分页方式性能优点缺点场景
from + size灵活性好,实现简单深度分页问题数据量比较小,能容忍深度分页问题
scroll解决了深度分页问题

无法反应数据的实时性(快照版本)

维护成本高,需要维护一个 scroll_id

海量数据的导出(比如笔者刚遇到的将es中20w的数据导入到excel)

需要查询海量结果集的数据

search_after

性能最好

不存在深度分页问题

能够反映数据的实时变更

实现复杂,需要有一个全局唯一的字段

连续分页的实现会比较复杂,因为每一次查询都需要上次查询的结果

海量数据的分页

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/651462
推荐阅读
相关标签
  

闽ICP备14008679号