【ElasticSearch】看懂Search After怎么分页

【ElasticSearch】看懂Search After怎么分页

深度分页是什么

  • 普通的分页方式是,通过**from(从哪条数据开始查) 和 size(查几条)**直接分页

  • 先了解一下elasticsearch的数据存储方式

    • elasticsearch的数据一般会采用分片存储,也就是把一个索引中的数据分成N份,存储到不同节点上,而且分片并不是按顺序分片的(不能直接根据分片顺序来分页)
  • 引入一个场景:比如一个索引库中有 100000 条数据,分别存储到4个分片,每个分片25000条数据。现在每页查询10条,查询第99页。那么分页查询的条件如下:

    GET /items/_search
    {
      "from": 990, // 从第990条开始查询
      "size": 10, // 每页查询10条
      "sort": [
        {
          "price": "asc"
        }
      ]
    }
    

    从语句来分析,要查询第990~1000名的数据。

    从实现思路来分析,肯定是将所有数据排序,找出前1000名,截取其中的990~1000的部分。但问题来了,我们如何才能找到所有数据中的前1000名呢?

    要知道每一片的数据都不一样,第1片上的第900-1000,在另1个节点上并不一定依然是900-1000名。所以我们只能在每一个分片上都找出排名前1000的数据,然后汇总到一起,重新排序(把每一个分片的前1000名汇总),才能找出整个索引库中真正的前1000名,此时截取990~1000的数据即可。

  • 假如我们现在要查询的是第999页数据呢,是不是要找第9990~10000的数据,那岂不是需要把每个分片中的前10000名数据都查询出来,汇总在一起,在内存中排序?如果查询的分页深度更深呢,需要一次检索的数据岂不是更多?

    由此可知,当查询分页深度较大时,汇总数据过多,对内存和CPU会产生非常大的压力。

    因此elasticsearch会禁止from+ size 超过10000的请求。

  • 针对深度分页,elasticsearch提供了两种解决方案:

    • search after:分页时需要排序,原理是从上一次的排序值开始,查询下一页数据。官方推荐使用的方式。
    • scroll:原理将排序后的文档id形成快照,保存下来,基于快照做分页。官方已经不推荐使用。

[!IMPORTANT]

Search After 的核心思想

Search After 不是一个“跳转到任意页面”的解决方案,而是一个“无限滚动”或“连续浏览”的解决方案。

  • aftersearch得到第一页的方式和普通的分页一样,后面的分页多了一个筛选条件,即上一页最后一个数据的信息
  • 对于上述场景如,要得到第99页(每页10条文档)的10调文档
    • 普通分页:每个分片拿出自己的前1000条数据,随后进行组合,再进行排序(在内存中进行),再挑出 991-1000条数据,得到每一页都是如此,每一次分页的压力都很大
    • search after(缺点是不能跳到别的页):
      • 得到第一页的方式与普通分页方式相同(每个分片拿出自己的前10条数据,随后进行组合,再进行排序(在内存中进行),再挑出 1-10条数据),记录下第一页的页尾信息
      • 接下来要得到第二页时,每一个分片都选出在第一页 页尾 后的 10条数据,随后进行组合,再进行排序(在内存中进行),再挑出 1-10条数据,记录下第二页的页尾信息,这样每次分页的成本不高。
        如果顺序翻页,速度很快。
        但是不支持跳跃翻页,比如从第1页直接跳到100页

总结:

大多数情况下,我们采用普通分页就可以了。查看百度、京东等网站,会发现其分页都有限制。例如百度最多支持77页,每页不足20条。京东最多100页,每页最多60条。基本上没人看这么多页

因此,一般我们采用限制分页深度的方式即可,无需实现深度分页。

### Elasticsearch分页使用 `search_after` 方法 对于大规模数据集中的分页查询,Elasticsearch 推荐使用 `search_after` 参数来实现高效的深分页操作。相比传统的 `from/size` 方式,`search_after` 可以显著提高性能并减少资源消耗。 #### 使用 `search_after` 的基本原理 当执行带有排序字段的搜索请求时,可以利用上一页最后一个文档的排序值作为下一次查询的起点。这通过传递 `_source` 中指定的一个或多个排序字段及其对应的值给 `search_after` 来完成[^1]。 #### 请求体结构 为了更好地理解如何构建这样的查询,下面是一个具体的例子: ```json { "sort": [ {"timestamp": "asc"}, "_doc" ], "search_after": [1587234000000, "AWqJf_6zZQaBwGgHjKlM"], "size": 10, "query": { "match_all": {} } } ``` 在这个 JSON 对象里: - `"sort"` 定义了用于排序的标准; - `"search_after"` 数组包含了前一页面最后一条记录按照相同顺序排列后的具体数值; - `"size"` 表示每页返回的结果数量; 请注意,在实际应用中应当替换掉这里的占位符(如时间戳和文档 ID),以便指向真实的上下文位置。 #### 实际调用方式 可以通过 HTTP GET 或 POST 发送上述定义好的 JSON 至目标索引路径下的 `_search` 终端节点。例如,采用 cURL 工具发送命令如下所示: ```bash curl -XPOST 'http://localhost:9200/twitter/tweet/_search?pretty' -d ' { "sort": [{"timestamp":"asc"}, "_doc"], "search_after":[1587234000000,"AWqJf_6zZQaBwGgHjKlM"], "size":10, "query":{ "match_all":{} } }' ``` 此方法不仅适用于简单的全量匹配场景,也支持更复杂的条件过滤逻辑。只要保持每次请求之间的一致性和连续性即可获得良好的用户体验与稳定的服务响应速度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值