Elasticsearch深度分页问题

xiaojiuaigc@163.com 发布于 2025-07-02 125 次阅读


比如一个索引库中有100000条数据,分别存储到4个分片,每个分片25000条数据。现在每页查询10条,查询第100页。那么分页查询的条件如下:

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名,此时截取990~1000的数据即可。

如图:

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

由此可知,当查询分页深度较大时,汇总数据过多,对内存和CPU会产生非常大的压力,特别是在 from 值非常大的情况下。这是因为Elasticsearch需要先跳过前面的所有文档才能获取到所需的文档,这可能导致大量的磁盘I/O操作和CPU使用率。

因此elasticsearch会限制from+ size 请求:

  1. size 参数的最大值:
  2. 默认情况下,size 参数的最大值被限制为 10,000。这意味着每次查询最多只能返回 10,000 条记录。
  3. from 参数的最大值:
  4. 默认情况下,from 参数的最大值也被限制为 10,000。这意味着您不能请求超过第 10,000 条记录之后的数据。

这意味着,理论上,您可以查询的最大页数是 10,000 / size。例如,如果 size 设置为 100,则最多可以查询 100 页。

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

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

search after举例:

查询第一页:

GET /items/_search
{
  "track_total_hits": true, 
  "query": {
    "match": {
      "name": "手机"
    }
  },
  "sort": [
    {
      "_id": {
        "order": "asc"
      }
    }
  ], 
  "size": 10,
  "search_after": ["0"]
}