前面的文章提到过es默认的from+size的分页方式返回的结果数据集不能超过1万点,超过以后返回的数据越多性能就越低。并发
这是由于es要计算类似度排名,须要排序整个整个结果集,假设咱们有一个index它有5个shard,如今要读取1000到1010之间的这10条数据,es内部会在每一个shard上读取1010条数据,而后返回给计算节点,这里有朋友可能问为啥不是10条数据而是1010条呢?这是由于某个shard上的10条数据,可能尚未另外一个shard上top10以后的数据类似度高,因此必须所有返回,而后在计算节点上,从新对5050条数据进行全局排序,最后在选取top 10出来,这里面排序是很是耗时的,因此这个数量实际上是指数级增加的,到后面分页数量越多性能就越降低的厉害,并且大量的数据排序会占用jvm的内存,颇有可能就OOM了,这也是为何es默认不容许读取超过1万条数据的缘由。jvm
那么问题来了,我就是想要深度的分页数据应该怎么办? es里面提供了两种方式来读取深度分页的数据:elasticsearch
(1)离线的读取深度分页数据的Scroll方法高并发
(2)可以用于实时和高并发场景的searchAfter方法(5.x以后)性能
Scroll方式在前面的文章提到过,它经过一次查询请求后维护一个索引快照的search context,而后每次再去批量的读取数据,效率比较高。在5.x以后,还能够经过slice分片来实现并行导出。code
它的缺点就是维护一个search context须要占用不少资源,并且在快照创建以后数据变化如删除和更新操做是不能被感知到的,因此不可以用于实时和高并发的场景。排序
searchAfter的方式经过维护一个实时游标来避免scroll的缺点,它能够用于实时请求和高并发场景。索引
它的缺点是不可以随机跳转分页,只能是一页一页的向后翻,而且须要至少指定一个惟一不重复字段来排序。内存
此外还有一个与scorll的不一样之处是searchAfter的读取数据的顺序会受索引的更新和删除影响而scroll不会,由于scroll读取的是不可变的快照。资源
下面来看下如何使用searchAfter:
咱们先查询一页数据:
GET twitter/_search { "size": 10, "query": { "match" : { "title" : "elasticsearch" } }, "sort": [ {"date": "asc"}, {"_id": "desc"} ] }
注意,上面用了两个字段来排序,第一个是业务字段可能不惟一,可是第二个id字段必定惟一不重复的。只有这样才能确保searchAfter的翻页顺序读取。
另外searchAfter的from字段必定要设置成0,否则会有问题。
第一个请求发出以后,咱们须要获取第一个请求里面最后一条的数据的date和id,而后把这个信息传送到下一个批次,依次类推直到把全部的数据处理完。
以下第二个请求的查询体:
GET twitter/_search { "size": 10, "query": { "match" : { "title" : "elasticsearch" } }, "search_after": [1463538857, "654323"], "sort": [ {"date": "asc"}, {"_id": "desc"} ] }
总结:
本篇文章介绍了如何在es里面使用深度分页的功能,并对比了scroll和searchAfter的优缺点及不一样之处,了解这些知识以后,咱们就能够在适合的场景下正确的选择最优的处理方式。