文档索引
本文档描述了如何将文档添加到索引中。
组件
Document
- 包含实际文档及其字段。RSAddDocumentCtx
- 索引文档时使用的每文档状态。索引完成后,该状态会被丢弃。ForwardIndex
- 包含文档中找到的术语。正向索引用于写入InvertedIndex
。InvertedIndex
- 将术语映射到适用文档中出现次数的索引。
架构
索引过程从创建新的 RSAddDocumentCtx
并向其中添加文档开始。在内部,这分为几个步骤。
-
提交
创建一个
DocumentContext
,并将其与从输入接收到的文档关联。提交过程还会执行一些初步缓存。 -
预处理
文档提交后,会对其进行预处理。预处理对所有文档输入字段执行无状态处理。对于文本字段,这意味着对文档进行分词并创建正向索引。预处理器将把此信息存储在
AddDocumentCtx
中的每字段变量中。然后,在索引阶段,计算出的结果会被写入持久索引中。如果文档足够大,预处理会在单独的线程中完成,这样可以实现并发预处理,并避免阻塞其他线程。如果文档较小,预处理会在主线程中完成,从而避免额外的上下文切换开销。
SELF_EXC_THRESHOLD
宏包含了“足够大”的阈值。文档预处理完成后,会提交进行索引。
-
索引
实际的索引过程包括提交预处理阶段计算出的结果。它在单个线程中完成,并采用队列的形式。
由于文档必须按照其文档 ID 分配的精确顺序写入索引,并且索引过程还必须让步于其他潜在的索引过程,因此您可能会遇到文档 ID 写入索引时乱序的情况。为了解决这个问题,文档实际写入的顺序必须明确定义。如果只有一个线程写入文档,则该线程在写入时无需担心 ID 乱序问题。
拥有一个单独的后台线程还有助于在多个方面进行优化,这将在后面看到。基本思想是,当有大量文档排队等待索引线程处理时,索引线程可以将其视为批量命令,从而大大减少 GIL 的锁定/解锁次数以及术语键需要打开和关闭的次数。
-
跳过已索引的文档
以下阶段可以同时处理多个文档。当文档完全索引后,它会被标记为已完成。当线程遍历队列时,它只会对尚未标记为已完成的项执行处理/索引操作。
-
术语合并
术语合并,或者说是正向索引合并,是在队列中有多个文档时进行的。扫描队列中每个文档的正向索引,并在其位置构建一个更大的主正向索引。主正向索引中的每个条目都包含对原始文档的引用以及正常的偏移量/得分/频率信息。
创建主正向索引可以避免每个文档多次打开共同的术语键。
如果队列中只有一个文档,则不会创建主正向索引。
请注意,主正向索引的内部类型实际上不是
ForwardIndex
。 -
文档 ID 分配
此时,GIL 被锁定,队列中的每个文档都会被分配一个文档 ID。分配紧接着在写入索引之前进行,以减少 GIL 被锁定的次数;因此,GIL 只会被锁定一次,就在写入索引之前。
-
写入索引
在 GIL 被锁定的情况下,任何待定的索引数据都会写入索引中。这通常涉及打开一个或多个 Redis 键,并将计算出的数据写入/复制到这些键中。
完成后,会发送给定文档的回复,并释放
AddDocumentCtx
。