如何理解elasticsearch的search after方案解决深度分页?
与传统的from
+ size
方法相比,search_after
避免了跳过大量记录的开销,因此在深度分页时效率更高
from
+ size
= 页码 * size ,其和在elasticsearch中默认限制小于10000
(随机跳页码,每次需要跳过大量数据,会对内存造成很大的影响,所以类似于京东,淘宝等,都会设置一个页码范围)
一、探究实际问题:
假设有3个分片,每个分片包含100条数据,总共有300条数据。假设每个分片的排名如下:
Shard1:包含排名1到100的数据
Shard2:包含排名101到200的数据
Shard3:包含排名201到300的数据
第一次查询:查询前10条数据
我们首先执行查询来获取前10条数据。由于我们对每个分片都进行排序查询,Elasticsearch会分别在所有分片中查找数据,然后合并并排序这些数据。
- 查询条件:
size=10
- 排序字段:
timestamp
(升序)
- Elasticsearch处理过程:
- Elasticsearch会查询所有3个分片(
Shard1
、Shard2
、Shard3
),并根据timestamp
字段进行排序。 - 结果
- 因为
Shard1
包含排名1到100的数据,它会提供前100条数据中的最小的10条数据。 Shard2
和Shard3
的数据排名较高,所以它们的前10条数据(排名101到200和201到300的排名)不会出现在这一页。
- 因为
- Elasticsearch会查询所有3个分片(
- 查询结果:
- 返回的是**
Shard1
中的排名1到10的记录**(这10条数据是你查询的前10条数据)。
- 返回的是**
第二次查询:查询排名10-20的数据(基于search_after
)
我们要查询第二页的数据,也就是排名第10到第20的数据。为了实现这一点,我们需要使用上一次查询的最后一条记录的排序信息(即timestamp
)来作为search_after
参数。
-
上一页的最后一条记录:
- 上一次查询的最后一条记录的
timestamp
是Shard1
中的第10条记录。假设它的timestamp
是2025-02-20T10:30:00
。
- 上一次查询的最后一条记录的
-
查询条件:
size=10
- 排序字段:
timestamp
(升序) search_after=["2025-02-20T10:30:00"]
-
Elasticsearch处理过程:
- Elasticsearch会从所有3个分片中查找
timestamp
大于2025-02-20T10:30:00
的数据。 - 因为
Shard1
中的前10条数据已经被返回了,所以第二页查询将从Shard1
中的第11条记录开始。除此之外,Shard2
和Shard3
中的数据也可能被查询到。 - 结果
Shard1
中的数据,排名11到20的数据(即timestamp
大于2025-02-20T10:30:00
的前10条数据)会出现在结果中。Shard2
中的数据,排名101到110的数据,也会被返回,因为它们的timestamp
字段值肯定大于2025-02-20T10:30:00
。Shard3
中的数据,排名201到210的数据,也会被返回,因为它们的timestamp
字段值也会大于2025-02-20T10:30:00
。
- Elasticsearch会从所有3个分片中查找
-
查询结果:
- 这时,你会得到**
Shard1
的排名11到20的数据**(这些数据在Shard1
中排在第11到20名)。 - 然后你会得到**
Shard2
的排名101到110的数据**,以及**Shard3
的排名201到210的数据**。
但由于
size=10
,Elasticsearch会对所有分片返回的结果进行排序,并且只返回最符合条件的10条记录。 - 这时,你会得到**
第三次查询:查询排名20-30的数据(基于search_after
)
接下来,我们使用上一次查询的最后一条记录的timestamp
来查询下一页,即排名20到30的数据。
- 上一页的最后一条记录:
- 假设第二页查询的最后一条记录的
timestamp
是2025-02-20T10:45:00
(Shard1
中的第20条记录的timestamp
)。
- 假设第二页查询的最后一条记录的
- 查询条件:
size=10
- 排序字段:
timestamp
(升序) search_after=["2025-02-20T10:45:00"]
- Elasticsearch处理过程:
- Elasticsearch会从所有3个分片中查找
timestamp
大于2025-02-20T10:45:00
的数据。 - 由于
Shard1
的排名1到20的数据已经被返回了,所以第二页查询将从Shard1
中的第21条记录开始。Shard2
和Shard3
的数据也可能被查询到。 - 结果
Shard1
中的数据,排名21到30的数据(即timestamp
大于2025-02-20T10:45:00
的前10条数据)会出现在结果中。Shard2
中的数据,排名111到120的数据,也会被返回,因为它们的timestamp
字段值肯定大于2025-02-20T10:45:00
。Shard3
中的数据,排名211到220的数据,也会被返回,因为它们的timestamp
字段值也会大于2025-02-20T10:45:00
。
- Elasticsearch会从所有3个分片中查找
- 查询结果:
- 你会得到**
Shard1
的排名21到30的数据**。 - 然后你会得到**
Shard2
的排名111到120的数据**,以及**Shard3
的排名211到220的数据**。
- 你会得到**
二、最后讲一个类比理解:
假设你在看一本书,你已经翻到某一页(例如第10页),然后你想继续看第11页。search_after
就像是“给你上一页的最后一行文字”,让你知道接下来你应该从哪里开始读,而不是从头开始翻书(就像from
那样跳过很多页)。
三、总结
优点:
① 提高查询性能:与传统的from
+ size
方法相比,search_after
避免了跳过大量记录的开销,因此在深度分页时效率更高。
② 无顺序问题:search_after
依赖排序键来确定结果的顺序,因此在进行分页时,即使数据在中间发生了变化,也不会错乱分页的顺序。
③ 适用于大数据量场景:当结果集非常大,传统的分页方法可能会对内存造成压力,search_after
能有效避免这种情况,适合处理大数据集。
注:
大多数情况下,我们采用普通分页就可以了。查看百度、京东等网站,会发现其分页都有限制。例如百度最多支持77页,每页不足20条。京东最多100页,每页最多60条。
因此,一般我们采用限制分页深度的方式即可,无需实现深度分页