可以使用一个名为 indexer 的特殊工具从外部源创建一个普通表,该工具从配置中读取“配方”,连接到数据源,拉取文档,并构建表文件。这是一个耗时的过程。如果数据发生变化,表就会过时,需要从更新后的源重新构建。如果数据是增量变化的,比如博客或新闻源,旧文档永远不变,只添加新文档,那么重建将花费越来越多的时间,因为每次都需要反复处理归档源。
解决这个问题的一种方法是使用多个表,而不是一个完整的表。例如,可以处理前几年产生的源并保存表。然后,只取当前年的源放入一个单独的表中,按需重建。然后可以将这两个表作为分布式表的部分放置,并用于查询。关键是每次重建时,最多只处理最近12个月的数据,旧数据的表保持不变,无需重建。还可以进一步将最近12个月的表划分为月表、周表或日表,等等。
这种方法有效,但需要手动维护分布式表。也就是说,添加新的分片,删除旧的分片,并保持部分表的总数不过多(表太多时,搜索可能变慢,且操作系统通常限制同时打开的文件数)。为此,可以通过运行 indexer --merge 手动合并多个表。然而,这只解决了表数量过多的问题,使维护更具挑战性。即使是“每小时”重新索引,也很可能存在新数据到达源和重建表之间的明显时间差,而表用于搜索的数据就是在重建时填充的。
实时表旨在解决这个问题。它由两部分组成:
- 一个特殊的基于内存的表(称为 RAM 分片),包含当前到达的数据部分。
- 一组普通表,称为磁盘分片,是过去构建的。
这与标准的分布式表非常相似,由多个本地表组成。
你不需要通过运行 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块被转换为磁盘块,或者两个磁盘块被合并时,服务器会对受影响的部分进行全面迭代,并从所有部分物理排除已删除的文档。也就是说,如果它们在某些词的文档列表中——它们会被清除。如果是唯一词——它会被完全移除。
总结:删除分两个阶段进行:
- 首先,我们在实时中将文档标记为“已删除”,并在搜索结果中抑制它们。
- 在对RT表块进行某些操作时,我们最终物理地彻底清除已删除的文档。
第五,如果RT表的集合中包含普通磁盘表,我能否直接将我准备好的旧磁盘表添加进去? 不能。这样做无法避免不必要的复杂性,也无法防止意外损坏。然而,如果你的RT表刚创建且不包含数据,那么你可以ATTACH TABLE你的磁盘表到它。你的旧表将被移动到RT表内部,成为其一部分。
关于RT表结构的总结:它是一个巧妙组织的普通磁盘表集合,配有一个快速的内存表,旨在实现文档的实时插入和半实时删除。RT表具有统一的模式、统一的设置,并且可以轻松维护,无需深入细节。
FLUSH RAMCHUNK rt_table
FLUSH RAMCHUNK 命令在 RT 表中创建一个新的磁盘块。
通常,当满足某个特殊条件时,RT 表会自动刷新并将 RAM 块的内容转换为新的磁盘块。但在某些情况下,您可能希望手动触发刷新——FLUSH RAMCHUNK 语句允许您这样做。
- SQL
FLUSH RAMCHUNK rt;Query OK, 0 rows affected (0.05 sec)FLUSH TABLE rt_table
FLUSH TABLE 强制将 RT 表的 RAM 块内容刷新到磁盘。
实时表的 RAM 块 会在干净关闭时自动刷新到磁盘,或者每隔 rt_flush_period 秒定期刷新。
执行 FLUSH TABLE 命令不仅强制将 RAM 块内容写入磁盘,还会触发二进制日志文件的清理。
- SQL
FLUSH TABLE rt;Query OK, 0 rows affected (0.05 sec)随着时间的推移,RT 表可能会碎片化成多个磁盘块和/或被已删除但未清除的数据污染,影响搜索性能。在这些情况下,需要进行优化。基本上,优化过程是将成对的磁盘块合并,移除之前通过 DELETE 语句删除的文档。
从 Manticore 4 开始,这个过程默认自动进行。但是,你也可以使用以下命令手动启动表的紧凑。
OPTIMIZE TABLE table_name [OPTION opt_name = opt_value [,...]]
OPTIMIZE 语句将 RT 表添加到优化队列,该队列将在后台线程中处理。
- SQL
OPTIMIZE TABLE rt;默认情况下,OPTIMIZE 会将 RT 表的磁盘块合并到不超过逻辑 CPU 核心数乘以 2 的数量。
但是,如果表中有带 KNN 索引的属性,这个阈值会不同。在这种情况下,阈值设置为物理 CPU 核心数除以 2,以提升 KNN 搜索性能。
你也可以使用 cutoff 选项手动控制优化后的磁盘块数量。
其他选项包括:
- 服务器设置 optimize_cutoff 用于覆盖默认阈值
- 每表设置 optimize_cutoff
- SQL
OPTIMIZE TABLE rt OPTION cutoff=4;使用 OPTION sync=1(默认值为 0)时,命令会等待优化过程完成后才返回。如果连接中断,优化仍会在服务器上继续运行。
- SQL
OPTIMIZE TABLE rt OPTION sync=1;优化可能是一个耗时且 IO 密集的过程。为了减少影响,所有实际的合并工作都在一个特殊的后台线程中串行执行,OPTIMIZE 语句只是将任务添加到其队列中。优化线程可以进行 IO 限速,你可以通过 rt_merge_iops 和 rt_merge_maxiosize 指令分别控制每秒最大 IO 数和最大 IO 大小。
在优化过程中,被优化的 RT 表几乎始终在线且可用于搜索和更新。只有在成功合并一对磁盘块时,表会被短暂锁定,以便重命名旧文件和新文件并更新表头。
只要没有禁用 auto_optimize,表会自动优化。
如果你遇到意外的 SST 文件,或者希望集群中所有节点的表二进制完全一致,你需要:
- 禁用 auto_optimize。
- 手动优化表:
在某个节点上,从集群中删除表:
‹›- SQL
SQL📋⚙ALTER CLUSTER mycluster DROP myindex;优化表:
‹›- SQL
SQL📋⚙OPTIMIZE TABLE myindex;将表重新添加到集群:
‹›- SQL
SQL📋⚙ALTER CLUSTER mycluster ADD myindex;当表被重新添加时,优化过程中创建的新文件会被复制到集群中的其他节点。 其他节点上对表的任何本地更改都会丢失。
表数据的修改(插入、替换、删除、更新)应当:
- 延后执行,或
- 定向到正在运行优化过程的节点。
注意,在表不在集群中时,插入/替换/删除/更新命令应当不带集群名称前缀(对于 SQL 语句或 HTTP JSON 请求中的 cluster 属性),否则会失败。 一旦表重新加入集群,必须恢复对表的写操作并再次包含集群名称前缀,否则操作会失败。
在此过程中,任何节点上的搜索操作照常可用。