Search after解决ES深度分页问题

search_after 是 Elasticsearch 中用于实现深度分页的一种机制。相比于传统的 from 和 size 分页方式,search_after 更适合处理大数据集的分页查询,因为它避免了深度分页带来的性能问题。

1、search_after 的作用和意义

传统分页的问题
在 Elasticsearch 中,使用 from 和 size 进行分页时,每次查询都需要从索引的第一个文档开始扫描,直到找到 from 指定的位置。对于深度分页(例如 from=10000, size=10),这种方式会导致性能急剧下降,因为需要扫描大量文档。

search_after 的优势

  • 性能优化:search_after 通过基于排序值的游标机制,避免了从头扫描文档的开销。

  • 适合大数据集:特别适合需要分页查询大量数据的场景。

  • 实时性:search_after 是基于实时数据的,能够反映索引的最新状态。

适用场景

  • 需要分页查询大量数据(例如日志数据、时间序列数据)。

  • 需要实现“无限滚动”或“加载更多”功能。

  • 需要避免深度分页的性能问题。

2、search_after 的工作原理

search_after 的工作原理是基于排序字段的值。每次查询时,Elasticsearch 会返回一组排序值(sort 字段),下一次查询时可以使用这些值作为游标,从上次查询结束的位置继续查询。

注意事项

  • 必须指定一个或多个排序字段(sort)。

  • 排序字段的值必须是唯一的,否则可能会导致分页不准确。

  • 使用 search_after 时,from 参数必须设置为 0 或省略。

3、search_after 的使用方法

步骤
1:第一次查询:

  • 指定排序字段(例如 @timestamp 和 _id)。

  • 设置 size 参数,确定每页返回的文档数量。

  • 不设置 search_after 参数。

2:后续查询:

  • 使用上一次查询返回的最后一个文档的排序值作为 search_after 参数。

  • 继续指定相同的排序字段和 size 参数。

示例

假设有一个索引 logs,存储日志数据,字段包括 @timestamp 和 message。我们需要按时间顺序分页查询日志。

第一次查询

GET /logs/_search
{
  "size": 10,
  "sort": [
    { "@timestamp": "asc" },
    { "_id": "asc" }
  ]
}

响应结果

{
  "hits": {
    "hits": [
      {
        "_id": "1",
        "_source": {
          "@timestamp": "2023-10-01T00:00:00Z",
          "message": "Log entry 1"
        },
        "sort": [ "2023-10-01T00:00:00Z", "1" ]
      },
      {
        "_id": "2",
        "_source": {
          "@timestamp": "2023-10-01T00:01:00Z",
          "message": "Log entry 2"
        },
        "sort": [ "2023-10-01T00:01:00Z", "2" ]
      },
      ...
    ]
  }
}

第二次查询

使用第一次查询的最后一个文档的排序值作为 search_after 参数:

GET /logs/_search
{
  "size": 10,
  "sort": [
    { "@timestamp": "asc" },
    { "_id": "asc" }
  ],
  "search_after": [ "2023-10-01T00:01:00Z", "2" ]
}

响应结果

{
  "hits": {
    "hits": [
      {
        "_id": "3",
        "_source": {
          "@timestamp": "2023-10-01T00:02:00Z",
          "message": "Log entry 3"
        },
        "sort": [ "2023-10-01T00:02:00Z", "3" ]
      },
      {
        "_id": "4",
        "_source": {
          "@timestamp": "2023-10-01T00:03:00Z",
          "message": "Log entry 4"
        },
        "sort": [ "2023-10-01T00:03:00Z", "4" ]
      },
      ...
    ]
  }
}

4、注意事项

  • 排序字段的唯一性:如果排序字段的值不唯一,可能会导致分页不准确。因此,通常需要结合 _id 或其他唯一字段进行排序。

  • 实时性:search_after 是基于实时数据的,因此在分页过程中,如果有新文档插入或旧文档删除,可能会导致分页结果不一致。

  • 性能优化:尽量选择高效的排序字段(例如数值字段或日期字段),避免使用文本字段进行排序。

5、与传统分页的对比

特性fromsize 分页search_after 分页
性能深度分页性能差深度分页性能好
适用场景小数据集分页大数据集分页
实时性基于查询时的快照基于实时数据
实现复杂度简单需要维护排序值
内存占用高(需要缓存大量文档)低(仅缓存排序值)

6、总结

search_after 是 Elasticsearch 中用于实现高效深度分页的机制。它通过基于排序值的游标机制,避免了传统分页的性能问题,特别适合处理大数据集的分页查询。使用时需要注意排序字段的唯一性和实时性,并结合实际场景选择合适的排序字段。

### 使用 `search_after` 实现深度分页查询 在 Elasticsearch 中,当处理大量数据时,传统的基于 `_from` 和 `size` 参数的分页方法可能会遇到性能瓶颈。为了更高效地进行深分页查询,推荐使用 `search_after` 方法。 #### 查询准备 确保索引中的文档有一个或多个字段能够提供稳定的排序依据,并且这些字段组合起来能唯一标识每条记录[^1]。例如,在法律文件索引中可以选择 `lawId` 作为唯一的排序键: ```json { "sort": [ { "lawId": "asc" } ] } ``` #### 初次查询 首次执行查询时不需设置 `search_after` 参数,只需定义好排序规则并指定每次返回的最大文档数 (`size`) 即可。下面是一个简单的初次查询例子,这里假设我们希望获取前10篇按 `lawId` 排序的文章: ```json GET /my_index/_search { "size": 10, "sort": [ { "lawId": "asc" } ], "query": { "match_all": {} } } ``` #### 继续查询后续页面 对于之后的每一次新请求,则需要带上上一次响应里最后一项的排序值作为新的 `search_after` 参数的一部分。这使得系统可以从该位置继续读取下一组结果。比如如果第一次查询结束于 `lawId=1680207200000` 的文章,则第二次查询应如下所示构建: ```json GET /my_index/_search { "size": 10, "sort": [ { "lawId": "asc" } ], "search_after": [1680207200000], "query": { "match_all": {} } } ``` 值得注意的是,一旦选择了 `search_after` 方式来进行分页操作,就不应该再尝试通过调整 `_from` 来改变起始点;相反,应当始终让其保持默认状态 (即不显式设定)[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Elastic开源社区

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值