解释Elasticsearch中的分页
#编程 #教程 #database #bigdata
分页是网页演示文稿的常见技术。当有很多返回的数据时,要么减少后端的负载或改善用户体验,通常是一个好主意,是限制演示量并保留继续浏览的选项。

这是最常见的外观。

< [1] | 2 | 3 | 4 | ... >

用户可以知道当前页码,选择上一页或下一页,甚至直接跳到指定的页面。分页方法有三种类型。

  • 偏移分页
  • Keyset分页
  • 基于光标的分页

但本文是为了介绍Elasticsearch分页,因此我将简要描述这三个。

偏移分页

这是最常见的分页模式,让我们以SQL表示。

SELECT *
FROM table_name
LIMIT 10 OFFSET 20;

假设一个页面上有10个记录,然后我们可以通过此命令获得第三页的结果。使用LIMIT限制每个页面的记录数,然后使用OFFSET跳到指定的页面。

这种分页有许多优势。

  1. 实现非常容易,并且非常直观。
  2. 可以跳到随机页面。

但缺点也很明显。

  1. 性能是一个问题。

为什么存在性能问题?

在上面的示例中,看来我们只需要从数据库中获取10个记录,但实际上,数据库将获取30个记录并丢弃前20个记录。当OFFSET很大时,数据库仍将获取所有记录并创建大量开销。

Keyset分页

为了解决性能问题,我们采用了Keyset分页方法。

我们仍然以SQL为例,遵循上述情况,我们将每个页面的记录数限制为10,然后访问第三页。

SELECT *
FROM table_name
WHERE id > 20
ORDER BY id
LIMIT 10;

使用LIMIT限制每个页面的记录数与上述相同,只是我们首先按ORDER BY排序,然后通过WHERE而不是OFFSET设置起始位置。这样,我们可以在不使用30个记录的情况下获得第三页。

有几个必须关注的实施细节。

  1. 必须能够快速分类,在关系数据库的情况下,要排序的列必须具有索引。
  2. WHERE子句从何而来?它可以来自第一个页面的最后位置,也可以是通过某种存储机构的后端。

当然,这种分页有优势和缺点。优点如下。

  1. 正确实现(例如,在索引列上)时运行良好。
  2. 它可以在非常大的数据集上运行。

但是这些优点是有代价的。

  1. 难以实施
  2. 无法跳入随机页面

我相信第一个缺点很容易理解,毕竟,这不是一种非常直观的方法,而是通过工程方法确定的WHERE子句。

第二个缺点是跳到特定页面,必须有一个正确的WHERE子句,但是此WHERE子句取决于最后一页跳跃结果,无论是前端还是后端处理。因此,用户无法按照他们的意愿跳跃页面。

基于光标的分页

这是Keyset分页的高级版本,实际上是基于光标的分页的特殊情况。

光标是由工程侧定义的对象,用于标记分页启动的位置。光标有几种常见类型。

  1. 编码光标,例如base64。假设光标是eyJpZCI6IDIwfQ==,然后我们会发现它是JSON格式字符串,指定的id为20。
  2. 令牌光标,后端为每个搜索结果生成一个令牌,并存储令牌,前端可以使用令牌指定跳页,后端可以根据令牌知道从哪里开始。

无论使用哪个光标,后端仍然使用按键分页机制进行分页。

基于光标的分页的优点和缺点是完全从Keyset分页上继承的,但是光标具有额外的优势,光标可以存储更多信息,例如会话超时,用户特权等。

怎么样的Elasticsearch

上面的示例以SQL为例,但实际上Elasticsearch也支持这些方法。

这是偏移分页的示例。

GET /index_name/_search
{
  "from": 20,
  "size": 10,
  "query": {
    "match_all": {}
  }
}

类似于SQL的原理,from用于指定从哪里开始,而size用于限制每个页面的记录数。然后,当然,也存在可伸缩性的限制,此外,Elasticsearch直接限制了fromsize的最大量,以免避免表现不佳的查询,从而影响了群集的健康。

  • max_result_window:可以一次搜索的最大数据量,默认值为10000。如果from + size > 10000,它将直接返回错误。

如何解决它?使用关键字search_after进行打开分页。

GET index_name/_search
{
  "size": 10,
  "sort": [
    {
      "id": {
        "order": "asc"
      }
    },
  ],
  "query": {
    "match_all": {}
  },
  "search_after": [
    20
  ]
}

这是Elasticsearch上的Keyset分页的典型实现。

那么,Elasticsearch是否支持基于光标的分页?是的,与SQL不同,您必须在应用程序中实现自己的光标。 Elasticsearch已经有自己的光标机制,称为Scroll API

原则是创建当前查询的快照,并每次调用scroll跳到下一页。一个实用的例子看起来像以下一个。

POST index_name/_search?scroll=1m
{
  "size": 10,
  "sort": [
    {
      "id": {
        "order": "asc"
      }
    },
  ],
  "query": {
    "match_all": {}
  }
}

这将通过_scroll_id获得回复,然后我们可以通过使用此_scroll_id跳到下一页。

POST /_search/scroll
{
  "scroll": "1m",
  "scroll_id": "OXOXQQ=="
}

由于这是一个基于光标的分页,我们无法指定要跳到哪个页面,我们只能继续访问下一页,直到没有结果为止。

因为Scroll API是当前查询结果的快照,因此我们必须在完成后小心选择TTL或删除快照,否则它将占据硬盘驱动器空间。

DELETE /_search/scroll
{
  "scroll_id" : "OXOXQQ=="
}

在新版本的Elasticsearch中,不再建议使用滚动API进行深层分页,而是另一种新机制(在7.10之后发布),PIT (Point In Time)

坑与滚动API类似,但更灵活,并且可以更好地优化性能。

滚动API拍摄单个查询的快照,只能在该快照上跳下页面,但是Pit拍摄了当前数据集的快照,并且在获得快照后可以做任何事情,而不仅仅是跳页。

但是,这与分页无关,因此我不会在本文中潜入坑。

结论

通常,分页,偏移分页和钥匙集分页有两种主要类型,并且只有钥匙集分页可以在大数据上运行,但是如果您想随机跳下页面,只有偏移分页就可以了,这是一个折衷的权衡。

实际上,在大数据方案中,如果特征要求严格定义了最大页面,那么即使使用偏移分页也不会影响性能。

因此,选择实施方法不仅是技术决定,而且在功能要求方面更常见。尽管如此,工程师应该知道是否需要随机页面跳跃大数据而没有页面限制,该是时候说不了,请清楚地拒绝。