当前位置: 首页 > article >正文

【Elasticsearch】分页查询

在 Elasticsearch 中,分页技术是处理大量搜索结果时的关键功能,尤其在需要优化性能或处理深度分页时。以下是三种主要的分页方法: from/size 、 scroll  API 和  search_after ,以及它们的详细对比和适用场景。
---

1.`from/size`分页
原理
`from/size`是最简单的分页方法,通过指定`from`和`size`参数来跳过指定数量的文档并返回指定数量的结果。
实现细节

• `from`:跳过的文档数量。

• `size`:每页返回的文档数量。
示例
请求:

```json
GET /index_name/_search
{
  "from": 0,
  "size": 10,
  "query": {
    "match_all": {}
  }
}
```


响应:

```json
{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 100,
      "relation": "eq"
    },
    "max_score": 1.0,
    "hits": [
      {
        "_index": "index_name",
        "_type": "_doc",
        "_id": "1",
        "_score": 1.0,
        "_source": {
          "field1": "value1",
          "field2": "value2"
        }
      },
      // 其他文档...
    ]
  }
}
```

优点

• 简单易用,适合用户界面分页。

• 支持随机访问任意页。
缺点

• 性能随`from`值增大而下降。

• 默认限制为 10,000 条结果。
适用场景

• 结果数量较少的场景。

• 用户界面分页。
---

2.`scroll`API
原理
`scroll`API 通过创建一个快照(snapshot)来保持搜索上下文,允许遍历大量数据。
实现细节

1. 初始化搜索上下文:首次请求返回初始结果和`_scroll_id`。

2. 滚动请求:使用`_scroll_id`获取更多结果。

3. 清理上下文:完成滚动后需清理上下文。
示例
初始化请求:

```json
POST /index_name/_search?scroll=1m
{
  "size": 100,
  "query": {
    "match_all": {}
  }
}
```


响应:

```json
{
  "_scroll_id": "abc123...",
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1000,
      "relation": "eq"
    },
    "hits": [
      {
        "_index": "index_name",
        "_type": "_doc",
        "_id": "1",
        "_source": {
          "field1": "value1",
          "field2": "value2"
        }
      },
      // 其他文档...
    ]
  }
}
```


滚动请求:

```json
POST /_search/scroll
{
  "scroll": "1m",
  "scroll_id": "abc123..."
}
```

优点

• 处理大量数据。

• 不受 10,000 条结果限制。


缺点

• 需要更多资源来维护搜索上下文。

• 数据状态固定,不会反映后续更改。


适用场景

• 数据导出。

• 批量处理。


---

3.`search_after`分页


原理
`search_after`通过指定上一页最后一条文档的排序值来获取下一页结果。


实现细节

• 需要明确的排序字段。

• 每次请求基于上一页的排序值进行分页。


示例
初始查询:

```json
GET /index_name/_search
{
  "size": 10,
  "query": {
    "match_all": {}
  },
  "sort": [
    {"price": "desc"},
    {"created_at": "asc"}
  ]
}
```


响应:

```json
{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 100,
      "relation": "eq"
    },
    "hits": [
      {
        "_index": "index_name",
        "_type": "_doc",
        "_id": "1",
        "_sort": [129.99, "2023-10-23T12:00:00Z"],
        "_source": {
          "price": 129.99,
          "created_at": "2023-10-23T12:00:00Z"
        }
      },
      // 其他文档...
    ]
  }
}
```


下一页查询:

```json
GET /index_name/_search
{
  "size": 10,
  "query": {
    "match_all": {}
  },
  "sort": [
    {"price": "desc"},
    {"created_at": "asc"}
  ],
  "search_after": [129.99, "2023-10-23T12:00:00Z"]
}
```

优点

• 高效,适合深度分页。

• 每次请求独立,实时反映数据变化。


缺点

• 需要排序字段,否则无法使用。

• 数据变化可能导致结果不一致。


适用场景

• 按排序顺序分页。

• 深度分页。

• 实时性要求高的场景。


---

4.点-in-time(PIT)+`search_after`


原理
PIT 创建一个数据一致性视图,结合`search_after`实现稳定分页。


实现细节

1. 创建 PIT:获取 PIT ID。

2. 首次查询:使用 PIT ID 和排序字段。

3. 后续查询:更新`search_after`参数,保持 PIT ID。


示例
创建 PIT:

```json
POST /index_name/_pit?keep_alive=1m
```


响应:

```json
{
  "id": "pit_id_123..."
}
```


首次查询:

```json
GET /_search
{
  "size": 10,
  "query": {
    "match_all": {}
  },
  "sort": [
    {"@timestamp": "desc"},
    {"_id": "asc"}
  ],
  "pit": {
    "id": "pit_id_123..."
  }
}
```


后续查询:

```json
GET /_search
{
  "size": 10,
  "query": {
    "match_all": {}
  },
  "sort": [
    {"@timestamp": "desc"},
    {"_id": "asc"}
  ],
  "search_after": [1630000000000, "doc_id_123"],
  "pit": {
    "id": "pit_id_123...",
    "keep_alive": "1m"
  }
}
```

优点

• 数据一致性高。

• 结合`search_after`,适合深度分页。


缺点

• 资源消耗高。

• 需要管理 PIT 生命周期。


适用场景

• 需要数据一致性的深度分页。

---

总结对比

 分页方法 优点 缺点 适用场景                                       

 `from/size` - 简单易用<br>- 支持随机访问任意页 - 性能随 `from` 增大而下降<br>- 默认限制为 10,000 条结果<br>- 不适合深度分页 - 用户界面分页<br>- 结果数量较少的场景          

 `scroll` API - 处理大量数据<br>- 不受 10,000 条结果限制<br>- 适合批量处理 - 资源消耗高(维护搜索上下文)<br>- 数据状态固定(不会反映后续更改)<br>- 需要显式清理上下文 - 数据导出<br>- 批量处理<br>- 不需要实时性的场景  

 `search_after` - 高效(适合深度分页)<br>- 每次请求独立<br>- 实时反映数据变化 - 需要明确的排序字段<br>- 数据变化可能导致结果不一致<br>- 实现逻辑相对复杂 - 按排序顺序分页<br>- 深度分页<br>- 实时性要求高的场景  

 PIT + `search_after` - 数据一致性高<br>- 结合 `search_after`,适合深度分页 - 资源消耗高(维护 PIT 上下文)<br>- 需要管理 PIT 生命周期<br>- 实现复杂度高 - 需要数据一致性的深度分页<br>- 实时性要求高的场景  

---

详细解析与适用场景

1.`from/size`分页

优点:

• 简单易用:适合简单的分页需求,如用户界面分页。

• 支持随机访问:可以直接跳到任意页。

缺点:

• 性能问题:随着`from`值增大,性能会显著下降。

• 深度分页限制:默认情况下,`from + size`不能超过`index.max_result_window`(默认为 10,000)。

适用场景:

• 用户界面分页:适合前端分页需求,如网页或移动应用中的分页。

• 结果数量较少:适合分页逻辑简单且结果数量较少的场景。

示例响应:

```json

{

  "took": 1,

  "timed_out": false,

  "_shards": {

    "total": 1,

    "successful": 1,

    "skipped": 0,

    "failed": 0

  },

  "hits": {

    "total": {

      "value": 100,

      "relation": "eq"

    },

    "max_score": 1.0,

    "hits": [

      {

        "_index": "index_name",

        "_type": "_doc",

        "_id": "1",

        "_score": 1.0,

        "_source": {

          "field1": "value1",

          "field2": "value2"

        }

      },

      // 其他文档...

    ]

  }

}

```

---

2.`scroll`API

优点:

• 处理大量数据:不受 10,000 条结果限制。

• 高效:滚动请求仅加载下一批结果。

缺点:

• 资源消耗:滚动上下文会占用额外的内存和资源。

• 数据一致性:滚动请求返回的是初始搜索时的数据状态。

• 需要清理:必须在完成滚动后显式清理上下文。

适用场景:

• 数据导出:适合将大量数据导出到其他系统。

• 批量处理:适合需要处理大量数据的场景,如日志分析或数据迁移。

示例响应:

```json

{

  "_scroll_id": "abc123...",

  "took": 1,

  "timed_out": false,

  "_shards": {

    "total": 1,

    "successful": 1,

    "failed": 0

  },

  "hits": {

    "total": {

      "value": 1000,

      "relation": "eq"

    },

    "hits": [

      {

        "_index": "index_name",

        "_type": "_doc",

        "_id": "1",

        "_source": {

          "field1": "value1",

          "field2": "value2"

        }

      },

      // 其他文档...

    ]

  }

}

```

---

3.`search_after`分页

优点:

• 高效:不需要扫描所有前序文档。

• 深度分页:适合深度分页。

• 实时性:每次请求都是独立的,可以反映最新的数据状态。

缺点:

• 依赖排序:需要明确的排序字段。

• 结果一致性:如果数据在分页过程中发生变化,可能会导致结果不一致。

• 复杂性:需要管理排序值和`search_after`参数。

适用场景:

• 按排序顺序分页:适合需要按特定顺序分页的场景。

• 深度分页:适合需要分页浏览大量结果的场景。

• 实时性要求高:适合需要实时反映数据变化的场景。

示例响应:

```json

{

  "took": 1,

  "timed_out": false,

  "_shards": {

    "total": 1,

    "successful": 1,

    "skipped": 0,

    "failed": 0

  },

  "hits": {

    "total": {

      "value": 100,

      "relation": "eq"

    },

    "hits": [

      {

        "_index": "index_name",

        "_type": "_doc",

        "_id": "1",

        "_score": null,

        "_source": {

          "price": 129.99,

          "created_at": "2023-10-23T12:00:00Z"

        },

        "sort": [129.99, "2023-10-23T12:00:00Z"]

      },

      // 其他文档...

    ]

  }

}

```

---

4.点-in-time(PIT)+`search_after`

优点:

• 数据一致性:PIT 保持了搜索上下文,确保分页过程中数据状态一致。

• 高效:结合`search_after`,可以高效地分页浏览大量数据。

缺点:

• 资源消耗:PIT 会占用额外的内存和资源。

• 复杂性:需要管理 PIT 生命周期和上下文。

适用场景:

• 需要数据一致性:适合在分页过程中需要保持数据状态一致的场景。

• 深度分页:适合需要分页浏览大量结果的场景。

• 实时性要求高:适合需要实时反映数据变化的场景。

示例响应:

```json

{

  "id": "pit_id_123..."

}

```

首次查询响应:

 

```json

{

  "took": 1,

  "timed_out": false,

  "_shards": {

    "total": 1,

    "successful": 1,

    "skipped": 0,

    "failed": 0

  },

  "hits": {

    "total": {

      "value": 100,

      "relation": "eq"

    },

    "hits": [

      {

        "_index": "index_name",

        "_type": "_doc",

        "_id": "1",

        "_score": null,

        "_source": {

          "@timestamp": "2023-10-23T12:00:00Z",

          "field1": "value1"

        },

        "sort": [1630000000000, "doc_id_123"]

      },

      // 其他文档...

    ]

  }

}

```

---

总结

选择哪种分页方法取决于你的具体需求:

• 如果你只需要简单的分页逻辑且结果数量较少,可以使用`from/size`。

• 如果你需要处理大量数据且不需要实时性,可以使用`scroll`API。

• 如果你需要按排序顺序分页且实时性要求高,可以使用`search_after`。

• 如果你需要在分页过程中保持数据一致性,可以结合使用 PIT 和`search_after`。

希望这些内容能帮助你更好地理解和选择适合的分页方法!
 


http://www.kler.cn/a/552736.html

相关文章:

  • Pycharm打开的jupyter notebook无法在pycharm中关闭怎么解决
  • el-table树状表格,默认展开第一个节点的每一层
  • 2024亚马逊数据分析!
  • lambda表达式thenComparing使用示例
  • Ubuntu 下创建具有 root 权限用户
  • Elasticsearch AI Assistant 集成 DeepSeek,1分钟搭建智能运维助手
  • 在nodejs中使用RabbitMQ(七)实现生产者确认
  • 私域流量运营中用户价值提升策略研究——以开源AI智能名片2+1链动模式与S2B2C商城小程序为例
  • 1-13 tortoiseGit忽略文件与文件夹
  • 深度学习模型常用激活函数集合
  • 智能硬件定位技术发展趋势
  • HarmonyOS:使用List实现分组列表(包含粘性标题)
  • 中电金信:数字基础设施未来展望·行业定制与开源融合
  • JSON类型理解(前后端交互/内存对数据操作)
  • 微服务监控与Go服务性能分析
  • 级联选择器多选动态加载
  • 基于SpringBoot+Vue高校就业领航管理系统
  • ollama离线环境部署deepseek及对话网站开发
  • mybatis 结合oracle存储过程返回list
  • GPT-Sovits:语音克隆训练-遇坑解决