如何在elasticsearch里面使用深度分页功能

前面的文章提到过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的优缺点及不一样之处,了解这些知识以后,咱们就能够在适合的场景下正确的选择最优的处理方式。

相关文章
相关标签/搜索