≫ 保护和压缩表

实时表结构

可以使用一个名为 indexer 的特殊工具从外部源创建一个普通表,该工具从配置中读取“配方”,连接到数据源,拉取文档,并构建表文件。这是一个耗时的过程。如果数据发生变化,表就会过时,需要从更新后的源重新构建。如果数据是增量变化的,比如博客或新闻源,旧文档永远不变,只添加新文档,那么重建将花费越来越多的时间,因为每次都需要反复处理归档源。

解决这个问题的一种方法是使用多个表,而不是一个完整的表。例如,可以处理前几年产生的源并保存表。然后,只取当前年的源放入一个单独的表中,按需重建。然后可以将这两个表作为分布式表的部分放置,并用于查询。关键是每次重建时,最多只处理最近12个月的数据,旧数据的表保持不变,无需重建。还可以进一步将最近12个月的表划分为月表、周表或日表,等等。

这种方法有效,但需要手动维护分布式表。也就是说,添加新的分片,删除旧的分片,并保持部分表的总数不过多(表太多时,搜索可能变慢,且操作系统通常限制同时打开的文件数)。为此,可以通过运行 indexer --merge 手动合并多个表。然而,这只解决了表数量过多的问题,使维护更具挑战性。即使是“每小时”重新索引,也很可能存在新数据到达源和重建表之间的明显时间差,而表用于搜索的数据就是在重建时填充的。

实时表旨在解决这个问题。它由两部分组成:

  1. 一个特殊的基于内存的表(称为 RAM 分片),包含当前到达的数据部分。
  2. 一组普通表,称为磁盘分片,是过去构建的。

这与标准的分布式表非常相似,由多个本地表组成。

你不需要通过运行 indexer 来构建这样的表,indexer 会从配置读取“配方”和表数据源。相反,实时表提供了“插入”新文档和“替换”现有文档的能力。执行“插入”命令时,你将新文档推送到服务器。服务器从添加的文档构建一个小表,并立即上线。因此,在“插入”命令完成后,你可以在所有表部分中执行搜索,包括刚添加的文档。

搜索服务器会自动维护表,因此你无需担心。但你可能想了解一些关于“它是如何维护的”的细节。

首先,既然索引数据存储在内存中——断电怎么办? 我会丢失表吗?在完成之前,服务器会将新数据保存到一个特殊的“binlog”中。它由一个或多个文件组成,存储在持久存储上,随着你添加越来越多的更改而增大。你可以调整新查询(或事务)存储到 binlog 的频率,以及对 binlog 文件执行“同步”命令的频率,以强制操作系统将数据实际保存到安全存储。最谨慎的方法是每个事务后都刷新和同步。这是最慢但最安全的方法。最省资源的方法是完全关闭 binlog。这是最快的方法,但有丢失索引数据的风险。也提供了中间方案,比如每秒刷新/同步一次。

binlog 专门设计用于顺序保存新到达的事务;它不是表,不能被搜索。它只是一个保险策略,确保服务器不会丢失你的数据。如果发生突发中断,因软件或硬件问题崩溃,服务器将加载 RAM 分片的最新可用转储,然后重放 binlog,重复存储的事务。最终,它将达到最后一次更改时的相同状态。

其次,限制如何? 如果我想处理,比如说,10TB 的数据,但它根本放不下内存怎么办!实时表的内存是有限的,可以配置。当索引到一定量的数据时,服务器通过合并小事务来管理内存部分,保持它们的数量和总体大小较小。但这个过程有时会导致插入时延迟。当合并不再有效,且新插入达到内存限制时,服务器会将基于内存的表转换为存储在磁盘上的普通表(称为磁盘分片)。该表被添加到实时表第二部分的表集合中,并可在线访问。然后内存被刷新,空间被释放。

当内存中的数据安全保存到磁盘时,发生以下情况:

  • 服务器将收集的数据保存为磁盘表
  • 或在干净关闭时或通过手动刷新转储内存部分

该表的 binlog 就不再需要,因此被丢弃。如果所有表都已保存,binlog 将被删除。

第三,磁盘集合怎么办? 如果有很多磁盘部分会使搜索变慢,那么我手动以分布式表的方式制作它们,或者它们由RT表生成的磁盘部分(或“块”)有什么区别呢?嗯,在这两种情况下,你都可以将多个表合并为一个。例如,你可以合并昨天的每小时表,并保留一个昨天的“每日”表。手动维护时,你必须自己考虑模式和命令。使用RT表,服务器提供了OPTIMIZE命令,它做同样的事情,但让你远离不必要的内部细节。

第四,如果我的“文档”构成一个“迷你表”,我不再需要它了,我可以直接丢弃它。但如果它被“优化”了,即与大量其他文档混合在一起,我怎么撤销或删除它? 是的,索引文档是“混合”在一起的,没有简单的方法删除其中一个而不重建整个表。对于普通表,重建或合并只是正常的维护方式,而对于实时表,它只保持了操作的简单性,但不是真正的“实时”。为了解决这个问题,Manticore使用了一个技巧:当你删除一个由文档ID标识的文档时,服务器只是跟踪这个编号。与其他已删除文档一起,它们的ID被保存在所谓的kill-list中。当你搜索表时,服务器首先检索所有匹配的文档,然后剔除在kill-list中找到的文档(这是最基本的描述;实际上内部更复杂)。关键是——为了“即时”删除,文档实际上并未被删除,而只是被标记为“已删除”。它们仍然占据不同表结构中的空间,本质上是垃圾。影响排名的词统计也不受影响,这意味着它的工作方式正如声明的那样:我们在所有文档中搜索,然后仅从最终结果中隐藏标记为已删除的文档。当文档被替换时,意味着它在表的旧部分被杀死,并在最新部分重新插入。“通过killlist隐藏”的所有后果在这种情况下也适用。

当表的某个部分重建时,例如,当RAM块的一些事务(段)被合并,或者RAM块被转换为磁盘块,或者两个磁盘块被合并时,服务器会对受影响的部分进行全面迭代,并从所有部分物理排除已删除的文档。也就是说,如果它们在某些词的文档列表中——它们会被清除。如果是唯一词——它会被完全移除。

总结:删除分两个阶段进行:

  1. 首先,我们在实时中将文档标记为“已删除”,并在搜索结果中抑制它们。
  2. 在对RT表块进行某些操作时,我们最终物理地彻底清除已删除的文档。

第五,如果RT表的集合中包含普通磁盘表,我能否直接将我准备好的旧磁盘表添加进去? 不能。这样做无法避免不必要的复杂性,也无法防止意外损坏。然而,如果你的RT表刚创建且不包含数据,那么你可以ATTACH TABLE你的磁盘表到它。你的旧表将被移动到RT表内部,成为其一部分。

关于RT表结构的总结:它是一个巧妙组织的普通磁盘表集合,配有一个快速的内存表,旨在实现文档的实时插入和半实时删除。RT表具有统一的模式、统一的设置,并且可以轻松维护,无需深入细节。

Last modified: August 28, 2025