可以使用一个称为 indexer 的特殊工具从外部源创建一个普通表,该工具从配置中读取“配方”,连接数据源,拉取文档,并构建表文件。这是一个耗时的过程。如果数据发生变化,表就会过时,需要从更新后的源重新构建。如果数据是增量变化的,例如一个博客或新闻推送,其中旧文档从不变更,只添加新文档,那么重建将花费越来越多的时间,因为每次都需要一次又一次地处理存档源。
解决这个问题的一种方式是使用多个表,而不是一个单一的大表。例如,你可以处理前几年生成的源并保存表。然后,只取当前年的源放入单独的表中,并根据需要频繁重建。然后你可以将这两个表作为分布式表的部分放置并用于查询。关键在于每次重建时,你最多只处理最近12个月的数据,而含有旧数据的表保持不变,无需重建。你还可以更进一步,将最近12个月的数据表拆分为月表、周表或者日表,依此类推。
这种方法是有效的,但你需要手动维护分布式表。也就是说,需要添加新的分片,删除旧的,并保持部分表的总数不宜过多(表数量过多搜索可能变慢,操作系统通常限制同时打开的文件数量)。为应对这一点,你可以通过运行 indexer --merge 手动合并几个表。但这只解决了表太多导致维护困难的问题。即使采用“每小时”重建索引,你仍然很可能在新数据到达源和重建表时间之间存在明显的时间差,而这段时间内数据无法被搜索。
实时表的设计旨在解决该问题。它由两部分组成:
- 一个特殊的基于内存的表(称为 RAM 分片),包含当前正在到达的数据部分。
- 一组普通表,称为磁盘分片,是过去构建的。
这与标准的 分布式表 非常相似,由几个本地表组成。
你不需要通过运行 indexer 构建这样的表,indexer 是读取配置中的“配方”和表数据源。相反,实时表提供了“插入”和“替换”现有文档的能力。执行“插入”命令时,你将新文档推送到服务器。服务器立刻从添加的文档构建一个小表并在线发布。因此,在“插入”命令完成后,你可以对所有表部分(包括刚添加的文档)执行搜索。
搜索服务器自动维护该表,因此你不用担心。但你可能想了解一些“它是如何维护的”细节。
首先,由于索引数据存储在内存中——断电怎么办?我会丢失表么?其实,在完成之前,服务器会将新数据保存到一个特殊的“二进制日志(binlog)”中。它由一个或多个文件组成,这些文件存在于持久存储中,随着越来越多的更改而增大。你可以调节新查询(或事务)存储到 binlog 的频率,以及执行 binlog 文件上的“同步”命令的频率,以强制操作系统将数据安全地写入存储。最保守的方法是每笔事务后都刷新并同步。这是最慢但最安全的方式。最省资源的方法是完全关闭 binlog,这是最快的,但你有丢失索引数据的风险。中间的变种,比如每秒刷新/同步也是支持的。
binlog 是专门为顺序保存新到事务设计的;它不是表,不能被搜索。它仅仅是保险策略,确保服务器不会丢失你的数据。如果突发故障发生,软件或硬件问题导致崩溃,服务器将加载 RAM 分片的最新可用转储文件,然后重放 binlog,重复存储的事务。最终,服务器将达到最后一次更改时的状态。
其次,限制是什么?如果我想处理比如说 10TB 的数据,但内存装不下怎么办!实时表的内存容量是有限的且可配置。当索引到一定数据量时,服务器通过合并小事务管理内存表部分,保持事务数量和总体大小较小。然而,这有时会导致插入延迟。当合并不再改善状况,且新插入达到 内存限制 时,服务器会将基于内存的表转成存储在磁盘上的普通表(称为磁盘分片)。此表加入 RT 表第二部分的表集合中并上线。内存被刷新,空间被释放。
当内存中的数据安全保存到磁盘时,发生以下情况:
- 服务器将收集的数据保存为磁盘表
- 或在干净关机时或通过 手动刷新 将内存部分转储
该表对应的 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 强制将实时表的RAM块内容刷新到磁盘。
实时表的RAM块会在正常关闭时自动刷新到磁盘,或者每隔rt_flush_period秒周期性刷新。
执行 FLUSH TABLE 命令不仅会强制将RAM块内容写入磁盘,还会触发二进制日志文件的清理。
- SQL
FLUSH TABLE rt;Query OK, 0 rows affected (0.05 sec)随着时间的推移,RT表可能会变得碎片化,分散在多个磁盘块中,并且可能被删除但未清除的数据污染,从而影响搜索性能。在这种情况下,需要进行优化。本质上,优化过程会合并磁盘块对,移除使用DELETE语句之前已删除的文档。
从Manticore 4开始,默认情况下,此过程会自动发生参见服务器设置中的Searchd.md。但是,您也可以使用以下命令手动启动表压缩。
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;优化可能是一个耗时且I/O密集型的过程。为了最小化其影响,实际合并工作是在一个特殊的后台线程中串行执行的,而OPTIMIZE语句只是将其添加到队列中。优化线程可以进行I/O限制,并可以通过rt_merge_iops和rt_merge_maxiosize指令分别控制每秒的最大I/O次数和最大I/O大小。
在优化过程中,正在优化的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请求中的集群属性),否则将失败。 一旦表重新添加到集群,您必须恢复对该表的写入操作,并再次包含集群名称前缀,否则这些操作将失败。
在优化过程中,可以在任何节点上正常使用搜索操作。