Solr的cursorMark如何在无国籍的情况下解决深刻的分页?

时间:2016-09-22 03:25:13

标签: solr pagination full-text-search

之前已经提出过这个问题,但我不满意,需要进一步阐述。

这些是基本前提:

  1. cursorMark是无状态的。它不会在服务器上存储任何内容
  2. cursorMark是一个计算值,用于指示是否跳过文档
  3. 我不确定我是否理解它,但这是我如何阅读给定的解释。

    我的问题是:

    1. 如果cursorMark想知道要跳过哪些文件,那么这不是一个搜索?它基本上是通过运行一系列文档并提出问题来进行搜索,这是我正在寻找的还是我需要跳过这个?"

    2. 仍然与第一个问题有关,这个"文件序列"如何计算?是不是存储在内存中?或者是cursorMark创建存储在磁盘上的临时文件?

    3. 如果它不记得整个结果范围,它如何计算下一个cursorMark?

    4. 我所能看到的是,没有逃脱。

      您可以存储有关搜索结果的某些状态,也可以搜索每个网页请求。

      相关参考资料:

      cursorMark is stateless and how it solves deep paging

      How much time does the cursorMark is available on solr server

      http://yonik.com/solr/paging-and-deep-paging/

1 个答案:

答案 0 :(得分:1)

cursorMark不会影响搜索 - 搜索仍然按原样执行。 cursorMark不是一个索引或与实际搜索的执行方式相关,但它是一种允许通过大型数据集进行有效分页的策略。这也意味着你的第二个问题没有实际意义,因为它没有改变实际搜索的执行方式。

当您考虑Solr服务器的集群的情况时,例如在SolrCloud模式下运行时,cursorMark解决深度分页的原因变得明显。

我们假设您有四台服务器ABCD,并希望从第400行开始检索10个文档(我们假设一个服务器==一个较大集合的一个分片,以使这更容易)。

在常规情况下,您必须从检索开始(按排序顺序,因为每个节点将根据您的查询对其结果集进行排序 - 这与任何常规查询都不同,因为它将需要在本地进行排序),然后合并:

  • 来自服务器A的410份文件
  • 来自服务器B的410份文件
  • 来自服务器C的410个文件
  • 来自服务器D的410份文件

您现在必须浏览1640个文档才能找到实际结果集的内容,因为它可能就是您正在寻找的10个文档,所有文件都存在于服务器上C。或者可能是服务器B上的350,而服务器D上的其余部分。如果没有从每个服务器实际检索410个文档,就不可能说。结果集将被合并和排序,直到跳过400个文档并找到10个文档。

现在说你想要从第1行开始的10个文件 - 你必须从每个服务器检索1 000 010个文件,并通过4 000 040个文件的结果集进行合并和排序。随着服务器和文档数量的增加,您可以看到这种情况变得越来越昂贵,只是为了将起点增加10个文档。

相反,让我们假设你知道全局排序顺序(意味着返回的最后一个文档的词法排序值)是什么。 第一个查询,没有一个cursorMark,将与常规分页相同 - 从每个服务器获取前10个文档(因为我们从一开始就开始)结果集(而不是第一个例子中的位置400),我们每个服务器只需要10个。)

我们处理这40个文档(一个非常易于管理的大小),对它们进行排序并检索10个第一个文档,然后我们包含最后一个文档的全局排序键(cursorMark)。然后客户端包括这个"全局排序键"在请求中,我们可以说"好的,我们对在本文档前面排序的任何条目不感兴趣,因为我们已经显示了那些"。然后,下一个查询将执行:

  • 来自服务器A的10个文档,它们将在cursorMark
  • 之后排序
  • 来自服务器B的10个文档,它们将在cursorMark
  • 之后排序
  • 来自服务器C的10个文档,它们将在cursorMark
  • 之后排序
  • 来自服务器D的10个文档,它们将在cursorMark
  • 之后排序

现在我们只是从每个服务器返回10个文档,即使我们的cursorMark在分页深度上有一百万行。我们以前必须检索的地方,排序(好吧 - 我们可以假设它们从结果集中返回,所以我们必须通过结果集找到第一百万个条目,然后从集合中选择下一个,在检索它们并处理4 000 040个文档后,我们现在只需要检索40个文档并在本地对它们进行排序,以便返回实际的10个文档。

为了进一步解释cursorMark如何工作,让我们假设这种方法只适用于具有整数值的唯一列(因为这样可以更容易地显示cursorMark在内部表示的内容,以及为什么the uniqueKey has to be present in the sort)(如果uniqueKey不存在,如果cursorMark最终出现在具有多个相同的排序字段值的文档上,我们可能会随机结束丢失的文档):

A    B    C    D
1    2    3    4
8    7    6    5
9    10   11   12
13   14   15   16
17   20   21   22
18   23   25   27
19   24   26   28

我们从cursorMark 7开始请求4个值(rows = 4)。每个服务器然后可以查看其结果集(在内部排序,因为所有结果集都是),并从中检索4个值排序顺序为7之后的值:(<表示这是cursorMark之后的第一个值,+表示此文档包含在从节点返回的结果集中)

A    B    C    D
1    2    3    4
8  < 7    6    5
9  + 10 < 11 < 12 <
13 + 14 + 15 + 16 +
17 + 20 + 21 + 22 +
18   23 + 25 + 27 +
19   24   26   28

然后我们遍历返回的每个结果集,直到我们从顶部选择了四个文档:

8 (from A)
9 (from A)
10 (from B)
11 (from C)

我们包含最后一个文档的cursorMark:11。然后使用11作为cursorMark进行下一个请求,这意味着每个服务器可以从11之后的条目开始返回4个文档:

A    B    C    D
1    2    3    4
8    7    6    5
9    10   11   12 <
13 < 14 < 15 < 16 +
17 + 20 + 21 + 22 +
18 + 23 + 25 + 27 +
19 + 24 + 26 + 28

然后我们再次执行合并,按排序顺序选择前4个条目,并包含下一个cursorMark。

..这回答了第三个问题:它不需要知道全局状态,只需要从巨大的数据集中返回的下一个结果。