elasticsearch专栏:https://www.cnblogs.com/hello-shf/category/1550315.htmlhtml
es 默认采用的分页方式是 from+ size 的形式,在深度分页的状况下,这种使用方式效率是很是低的,好比咱们执行以下查询数据库
1 GET /student/student/_search 2 { 3 "query":{ 4 "match_all": {} 5 }, 6 "from":5000, 7 "size":10 8 }
意味着 es 须要在各个分片上匹配排序并获得5010条数据,协调节点拿到这些数据再进行排序等处理,而后结果集中取最后10条数据返回。api
咱们会发现这样的深度分页将会使得效率很是低,由于我只须要查询10条数据,而es则须要执行from+size条数据而后处理后返回。less
其次:es为了性能,限制了咱们分页的深度,es目前支持的最大的 max_result_window = 10000;也就是说咱们不能分页到10000条数据以上。 curl
例如:elasticsearch
from + size <= 10000因此这个分页深度依然可以执行。ide
继续看上图,当size + from > 10000;es查询失败,而且提示性能
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
有关请求大数据集的更有效方法,请参阅滚动api。这个限制能够经过改变[索引]来设置。哦呵,原来es给咱们提供了另外的一个API scroll。难道这个 scroll 能解决深度分页问题?ui
在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
在后面的请求中咱们都要带着这个 scroll_id 去请求。
如今student这个索引中共有6条数据,id分别为 1, 2, 3, 4, 5, 6。当咱们使用 scroll 查询第4次的时候,返回结果应该为kong。这时咱们就知道已经结果集已经匹配完了。
继续执行3次结果以下三图所示。
1 GET /_search/scroll 2 { 3 "scroll":"5m", 4 "scroll_id":"DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAC0YFmllUjV1QTIyU25XMHBTck1XNHpFWUEAAAAAAAAtGRZpZVI1dUEyMlNuVzBwU3JNVzR6RVlBAAAAAAAALRsWaWVSNXVBMjJTblcwcFNyTVc0ekVZQQAAAAAAAC0aFmllUjV1QTIyU25XMHBTck1XNHpFWUEAAAAAAAAtHBZpZVI1dUEyMlNuVzBwU3JNVzR6RVlB" 5 }
由结果集咱们能够发现最终确实分别获得了正确的结果集,而且正确的终止了scroll。
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 }
结果集:
{ "took" : 1, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : 6, "max_score" : null, "hits" : [ { "_index" : "student", "_type" : "student", "_id" : "6", "_score" : null, "_source" : { "uid" : 1006, "name" : "dehua", "age" : 27, "class" : "3-1" }, "sort" : [ 1006 ] }, { "_index" : "student", "_type" : "student", "_id" : "5", "_score" : null, "_source" : { "uid" : 1005, "name" : "fucheng", "age" : 23, "class" : "2-3" }, "sort" : [ 1005 ] } ] } }
下一次分页,须要将上述分页结果集的最后一条数据的值带上。
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 }
这样咱们就使用search_after方式实现了分页查询。
分页方式 | 性能 | 优势 | 缺点 | 场景 |
from + size | 低 | 灵活性好,实现简单 | 深度分页问题 | 数据量比较小,能容忍深度分页问题 |
scroll | 中 | 解决了深度分页问题 | 没法反应数据的实时性(快照版本) 维护成本高,须要维护一个 scroll_id |
海量数据的导出(好比笔者刚遇到的将es中20w的数据导入到excel) 须要查询海量结果集的数据 |
search_after | 高 | 性能最好 不存在深度分页问题 可以反映数据的实时变动 |
实现复杂,须要有一个全局惟一的字段 连续分页的实现会比较复杂,由于每一次查询都须要上次查询的结果 |
海量数据的分页 |
参考文献:
《elasticsearch-权威指南》
若有错误的地方还请留言指正。
原创不易,转载请注明原文地址:http://www.javashuo.com/article/p-ebrlqejs-bo.html