elasticsearch 分页查询实现方案

1. from+size 实现分页

from表示从第几行开始,size表示查询多少条文档。from默认为0,size默认为10,

注意:size的大小不能超过index.max_result_window这个参数的设置,默认为10,000。

如果搜索size大于10000,需要设置index.max_result_window参数

PUT _settings
{
    "index": {
        "max_result_window": "10000000"
    }
}

内部执行原理:

示例:有三个节点node1、node2、node3,每个节点上有2个shard分片

node1 node2 node3
shard1 shard3 shard5
shard2 shard4 shard6
1.client发送分页查询请求到node1(coordinating node)上,node1建立一个大小为from+size的优先级队列来存放查询结果;
2.node1将请求广播到涉及到的shards上;
3.每个shards在内部执行查询,把from+size条记录存到内部的优先级队列(top N表)中;
4.每个shards把缓存的from+size条记录返回给node1;
5.node1获取到各个shards数据后,进行合并并排序,选择前面的 from + size 条数据存到优先级队列,以便 fetch 阶段使用。

各个分片返回给 coordinating node 的数据用于选出前 from + size 条数据,所以,只需要返回唯一标记 doc 的 _id 以及用于排序的 _score 即可,这样也可以保证返回的数据量足够小。

coordinating node 计算好自己的优先级队列后,query 阶段结束,进入 fetch 阶段。

from+size在深度分页时,会带来严重的性能问题:

CPU、内存、IO、网络带宽

数据量越大,越往后翻页,性能越低

2.scroll

可以把 scroll 理解为关系型数据库里的 cursor,因此,scroll 并不适合用来做实时搜索,而更适用于后台批处理任务,比如群发。

可以把 scroll 分为初始化和遍历两步,

初始化时将所有符合搜索条件的搜索结果缓存起来,可以想象成快照,

遍历时,从这个快照里取数据,也就是说,在初始化后对索引插入、删除、更新数据都不会影响遍历结果。

1.初始化:

POST http://192.168.18.230:9200/bill/bill/_search?scroll=3m
{
    "query": { "match_all": {}},
    "size": 10
}

参数 scroll,表示暂存搜索结果的时间

返回一个 _scroll_id,_scroll_id 用来下次取数据用

2.遍历:

POST http://192.168.18.230:9200/_search?scroll=3m
{
  "scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAHRCFi1BLWIzSHdhUkl1cC1rcjBueVhJZUEAAAAAAAB0QRYtQS1iM0h3YVJJdXAta3IwbnlYSWVBAAAAAAAAdEQWLUEtYjNId2FSSXVwLWtyMG55WEllQQAAAAAAAHRDFi1BLWIzSHdhUkl1cC1rcjBueVhJZUEAAAAAAAB0RRYtQS1iM0h3YVJJdXAta3IwbnlYSWVB"
}

这里的 scroll_id 即 上一次遍历取回的 _scroll_id 或者是初始化返回的 _scroll_id,同样的,需要带 scroll 参数。

注意,每次都要传参数 scroll,刷新搜索结果的缓存时间。另外,不需要指定 index 和 type。

3.search_after

官网上的说明:

The Scroll api is recommended for efficient deep scrolling but scroll contexts are costly and it is not recommended to use it for real time user requests.
The search_after parameter circumvents this problem by providing a live cursor. The idea is to use the results from the previous page to help the retrieval of the next page.

Scroll 被推荐用于深度查询,但是contexts的代价是昂贵的,不推荐用于实时用户请求,而更适用于后台批处理任务,比如群发。

search_after 提供了一个实时的光标来避免深度分页的问题,其思想是使用前一页的结果来帮助检索下一页。

search_after 需要使用一个唯一值的字段作为排序字段,否则不能使用search_after方法

推荐使用_uid 作为唯一值的排序字段

GET twitter/tweet/_search
{
    "size": 10,
    "query": { "match_all": {}},
    "sort": [
        {"date": "asc"},
        {"_uid": "desc"}
    ]
}

每一条返回记录中会有一组 sort values ,查询下一页时,在search_after参数中指定上一页返回的 sort values

GET twitter/tweet/_search
{
    "size": 10,
    "query": { "match_all": {}},
    "search_after": [1463538857, "tweet#654323"],
    "sort": [
        {"date": "asc"},
        {"_uid": "desc"}
    ]
}

注意:search_after不能自由跳到一个随机页面,只能按照 sort values 跳转到下一页

4.总结

  • 深度分页不管是关系型数据库还是Elasticsearch还是其他搜索引擎,都会带来巨大性能开销,特别是在分布式情况下。
  • 有些问题可以考业务解决而不是靠技术解决,比如很多业务都对页码有限制,google 搜索,往后翻到一定页码就不行了。
  • scroll 并不适合用来做实时搜索,而更适用于后台批处理任务,比如群发。
  • search_after不能自由跳到一个随机页面,只能按照 sort values 跳转到下一页。

原文地址:https://www.cnblogs.com/jpfss/p/10819258.html

时间: 2024-10-11 00:30:36

elasticsearch 分页查询实现方案的相关文章

真正高效的SQLSERVER分页查询(多种方案)

Sqlserver数据库分页查询一直是Sqlserver的短板,闲来无事,想出几种方法,假设有表ARTICLE,字段ID.YEAR...(其他省略),数据53210条(客户真实数据,量不大),分页查询每页30条,查询第1500页(即第45001-45030条数据),字段ID聚集索引,YEAR无索引,Sqlserver版本:2008R2 第一种方案.最简单.普通的方法: 复制代码代码如下: SELECT TOP 30 * FROM ARTICLE WHERE ID NOT IN(SELECT TO

Elasticsearch分页查询

global index global CLIENT index = "guajibao-ipused-2019.10.13" CLIENT = Elasticsearch(hosts=es_host,port=port,timeout=timeout) def load_all_data(scroll_id): if scroll_id: page = CLIENT.scroll(scroll_id=scroll_id, scroll='10m') scroll_id = page[

数据库分页查询方法

在这里主要讲解一下MySQL.SQLServer2000(及SQLServer2005)和ORCALE三种数据库实现分页查询的方法. 可能会有人说这些网上都有,但我的主要目的是把这些知识通过我实际的应用总结归纳一下,以方便大家查询使用. 下面就分别给大家介绍.讲解一下三种数据库实现分页查询的方法. 一. MySQL 数据库分页查询 MySQL数据库实现分页比较简单,提供了LIMIT函数.一般只需要直接写到sql语句后面就行了. LIMIT子句可以用来限制由SELECT语句返回过来的数据数量,它有

Elasticsearch教程(九) elasticsearch 查询数据 | 分页查询

Elasticsearch  的查询很灵活,并且有Filter,有分组功能,还有ScriptFilter等等,所以很强大.下面上代码: 一个简单的查询,返回一个List<对象> .. /**     * 根据Id 查询 SOBangg     * @param key     * @return     */    public static List findSOBanggById(String id) {        Client client = ESTools.client;    

elasticsearch之分页查询

分页查询: from /size GET zhifou/doc/_search { "query": { "match_all": {} }, "sort": [ { "age": { "order": "desc" } } ], "from": 2, #从排序好的结果第二条开始 "size": 1 # 返回一条数据 } 原文地址:https://

HBase多条件及分页查询的一些方法

HBase是Apache Hadoop生态系统中的重要一员,它的海量数据存储能力,超高的数据读写性能,以及优秀的可扩展性使之成为最受欢迎的NoSQL数据库之一.它超强的插入和读取性能与它的数据组织方式有着密切的关系,在逻辑上,HBase的表数据按RowKey进行字典排序, RowKey实际上是数据表的一级索引(Primary Index),由于HBase本身没有二级索引(Secondary Index)机制,基于索引检索数据只能单纯地依靠RowKey.也只有使用RowKey查询数据才能得到非常高

Oracle分页查询

一.利用rownum,无order by(最优方案) 如下例查询出来5003行数据,然后扔掉了前面5000行,返回后面的300行.经过测试,此方法成本最低,只嵌套一层,速度最快!即使查询的数据量再大,也几乎不受影响,速度依然. SELECT * FROM (SELECT ROWNUM AS rowno, t.* FROM XXX t WHERE hire_date BETWEEN TO_DATE ('20060501', 'yyyymmdd') AND TO_DATE ('20060731',

MySQL、SqlServer、Oracle三大主流数据库分页查询 (MySQL分页不能用top,因为不支持)

一. MySQL 数据库 分页查询MySQL数据库实现分页比较简单,提供了 LIMIT函数.一般只需要直接写到sql语句后面就行了.LIMIT子 句可以用来限制由SELECT语句返回过来的数据数量,它有一个或两个参数,如果给出两个参数, 第一个参数指定返回的第一行在所有数据中的位置,从0开始(注意不是1),第二个参数指定最多返回行数.例如:select * from table WHERE … LIMIT 10; #返回前10行select * from table WHERE … LIMIT

SQLserver分页查询实例

Sqlserver数据库分页查询一直是Sqlserver的短板,闲来无事,想出几种方法,假设有表ARTICLE,字段ID.YEAR...(其他省略),数据53210条(客户真实数据,量不大),分页查询每页30条,查询第1500页(即第45001-45030条数据),字段ID聚集索引,YEAR无索引,Sqlserver版本:2008R2 第一种方案.最简单.普通的方法: SELECT TOP 30 * FROM ARTICLE WHERE ID NOT IN(SELECT TOP 45000 ID