这是最常见的外观。
< [1] | 2 | 3 | 4 | ... >
用户可以知道当前页码,选择上一页或下一页,甚至直接跳到指定的页面。分页方法有三种类型。
- 偏移分页
- Keyset分页
- 基于光标的分页
但本文是为了介绍Elasticsearch分页,因此我将简要描述这三个。
偏移分页
这是最常见的分页模式,让我们以SQL表示。
SELECT *
FROM table_name
LIMIT 10 OFFSET 20;
假设一个页面上有10个记录,然后我们可以通过此命令获得第三页的结果。使用LIMIT
限制每个页面的记录数,然后使用OFFSET
跳到指定的页面。
这种分页有许多优势。
- 实现非常容易,并且非常直观。
- 可以跳到随机页面。
但缺点也很明显。
- 性能是一个问题。
为什么存在性能问题?
在上面的示例中,看来我们只需要从数据库中获取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个记录的情况下获得第三页。
有几个必须关注的实施细节。
- 必须能够快速分类,在关系数据库的情况下,要排序的列必须具有索引。
-
WHERE
子句从何而来?它可以来自第一个页面的最后位置,也可以是通过某种存储机构的后端。
当然,这种分页有优势和缺点。优点如下。
- 正确实现(例如,在索引列上)时运行良好。
- 它可以在非常大的数据集上运行。
但是这些优点是有代价的。
- 难以实施
- 无法跳入随机页面
我相信第一个缺点很容易理解,毕竟,这不是一种非常直观的方法,而是通过工程方法确定的WHERE
子句。
第二个缺点是跳到特定页面,必须有一个正确的WHERE
子句,但是此WHERE
子句取决于最后一页跳跃结果,无论是前端还是后端处理。因此,用户无法按照他们的意愿跳跃页面。
基于光标的分页
这是Keyset分页的高级版本,实际上是基于光标的分页的特殊情况。
光标是由工程侧定义的对象,用于标记分页启动的位置。光标有几种常见类型。
- 编码光标,例如base64。假设光标是
eyJpZCI6IDIwfQ==
,然后我们会发现它是JSON格式字符串,指定的id
为20。 - 令牌光标,后端为每个搜索结果生成一个令牌,并存储令牌,前端可以使用令牌指定跳页,后端可以根据令牌知道从哪里开始。
无论使用哪个光标,后端仍然使用按键分页机制进行分页。
基于光标的分页的优点和缺点是完全从Keyset分页上继承的,但是光标具有额外的优势,光标可以存储更多信息,例如会话超时,用户特权等。
。。。怎么样的Elasticsearch
上面的示例以SQL为例,但实际上Elasticsearch也支持这些方法。
这是偏移分页的示例。
GET /index_name/_search
{
"from": 20,
"size": 10,
"query": {
"match_all": {}
}
}
类似于SQL的原理,from
用于指定从哪里开始,而size
用于限制每个页面的记录数。然后,当然,也存在可伸缩性的限制,此外,Elasticsearch直接限制了from
和size
的最大量,以免避免表现不佳的查询,从而影响了群集的健康。
- 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拍摄了当前数据集的快照,并且在获得快照后可以做任何事情,而不仅仅是跳页。
但是,这与分页无关,因此我不会在本文中潜入坑。
结论
通常,分页,偏移分页和钥匙集分页有两种主要类型,并且只有钥匙集分页可以在大数据上运行,但是如果您想随机跳下页面,只有偏移分页就可以了,这是一个折衷的权衡。
实际上,在大数据方案中,如果特征要求严格定义了最大页面,那么即使使用偏移分页也不会影响性能。
因此,选择实施方法不仅是技术决定,而且在功能要求方面更常见。尽管如此,工程师应该知道是否需要随机页面跳跃大数据而没有页面限制,该是时候说不了,请清楚地拒绝。