在这篇文章中,我们将讨论在Manticore Search 3中杀死列表是如何工作的。
普通索引在添加新文档方面是不可变的:一旦创建,就不可能添加更多数据,只能更新现有文档的属性。为了使索引与主要数据(可能是数据库或文件)保持一致,需要通过执行完全重建来刷新索引。此操作可能需要时间。在某些情况下,完全重建可能需要几个小时甚至更长时间。
这意味着可搜索的数据可能滞后于原始数据存储,最新的内容在索引重建之前需要等待时间。为了解决这个问题,引入了增量索引的概念。增量索引具有与更大(又称“主”)索引相同的结构,但它是用来捕捉在主索引构建后添加到数据库的文档。捕获点通常是文档ID或时间点。
增量索引捕获较新的文档(那些从未存在过的),在大多数情况下,还希望捕获已修改的文档。另一个常见的要求是能够以某种方式丢弃被删除的文档。已修改的文档可以包含在增量索引中,但它们引入了一个问题:在主索引和增量索引上搜索将会带来同一文档的两个版本——主索引中的旧版本和增量索引中的新版本——而引擎不知道该选择哪一个。
为了克服这些问题,添加了一个新概念:杀死列表——在增量索引中,已知被修改或删除的文档ID列表,以便索引在主索引中搜索时忽略它们。
到目前为止一切正常。然而,在v2中,引擎必须在将从主索引提取的结果集与增量索引的结果集合并之前,应用杀死列表。虽然通常这对许多用户来说效果良好,但在每个查询上应用杀死列表确实对性能有影响。这在小索引或小杀死列表上不明显,但在大型索引上会出现问题,这些索引“重建需要很长时间”,而增量索引可能会最终拥有大量的杀死列表集。
<img src="killlists-v2-optimized.webp" alt="img"> 2.x中的杀死列表
Manticore 3通过对索引存储引擎进行大规模升级引入了一个重大变化。杀死列表也需要更改。
此更改有两个原因:现有的杀死列表方式在一些边缘情况下被证明是有问题的,并且它也与新的存储引擎不太相符。
因此,不如在每次查询时应用杀死列表,而是一次性完成?在v3中,在增量索引的源中,我们仍然定义了一个杀死列表源(sql_query_killlist),但在索引配置中,我们需要定义杀死列表的目标。
当索引加载时,引擎会检查是否有需要应用的杀死列表。如果找到了一个(或多个),则将该杀死列表应用于索引,通过将匹配的文档标记为已删除。当在索引上执行查询时,这些标记的文档将被简单地忽略(就像它们不存在一样)。由于这种抑制已经在启动或索引轮换时完成,因此不再需要增量索引中存在被删除的文档,以便不再在结果中显示。
<img src="killlists-v3-optimized.webp" alt="img"> 杀死列表目标在 killlist_target 索引指令中定义。该指令期望一个索引列表和一个应用杀死列表的模式。目前支持3种模式:
kl - 从定义的杀死列表(由sql_query_killlist定义)的文档ID将用于抑制
id - 从索引中使用文档ID(任何定义的sql_query_killlist将被忽略)
或者默认情况下,如果不指定’kl’或’id’,则将使用索引中的文档ID和sql_query_killlist
示例
index delta {
...
killlist_target = index_one:kl, index_two:id
...
}
目标指令可以使用ALTER TABLE命令进行更改,但为了使其生效,目标索引必须重新加载。同时,ALTER无法“禁用”目标索引中已经删除的ID(通过将索引从目标列表中移除)。
新的杀死列表工作方式提高了查询性能,因为现在在查询时没有额外的操作来比较找到的文档ID与抑制列表。更重要的是,永久标记被抑制的文档ID意味着在每个查询中需要搜索的ID列表更短。它还增加了灵活性,因为不仅可以使用来自专用列表(由sql_killlist_query生成)的ID进行抑制,还可以根据需要使用整个索引的文档ID。