分摊加载
解析器可以使用多个数据库节点来并行加载单个输入源。此方法称为分摊加载。在 Vertica 内置的解析器中,默认(分隔)解析器支持分摊加载。
与协作解析一样,分摊加载也需要可以在记录边界进行拆分的输入。不同之处在于,协作解析会执行顺序扫描以查找记录边界,而分摊加载会先跳(寻找)至给定位置,然后再执行扫描。某些格式(例如通用 XML)不支持寻找。
要使用分摊加载,您必须确保所有参与的数据库节点都可以访问源。分摊加载通常与分布式文件系统结合使用。
解析器可以不直接支持分摊加载,但需要包含支持分摊的块分割器。
您可以单独或同时使用分摊加载和协作解析。请参阅组合协作解析和分摊加载。
Vertica 如何分摊加载
如果解析器及其源均支持分摊,则您可以指定将单个输入分发到多个数据库节点进行加载。SourceFactory
可将输入拆分为多个部分,并将它们分配给执行节点。每个 Portion
包含一个输入偏移量和一个大小。Vertica 会将各个部分及其参数分发到执行节点。在每个节点上运行的源工厂将为给定部分生成一个 UDSource
。
UDParser
首先会确定从何处开始解析:
-
如果该部分是输入中的第一部分,则解析器会前进至该偏移量并开始解析。
-
如果不是输入中的第一部分,则解析器会前进该偏移量,然后执行扫描直至找到记录的结尾。由于记录分散在多个部分,因此在到达第一个记录结尾之后开始解析。
解析器必须完成整个记录,这可能要求解析器在该部分的结尾之后继续读取。无论记录在何处结束,解析器都负责处理从已分配的部分开始的所有记录。此工作主要在解析器的 process()
方法中进行。
有时,一个部分不包含可由已分配的节点解析的任何内容。例如,假设某个记录从第 1 部分开始,而且贯穿第 2 部分并在第 3 部分结束。分配给第 1 部分的解析器将解析该记录,而分配给第 3 部分的解析器将在该记录之后开始解析。但是,分配给第 2 部分的解析器没有任何记录从此部分开始。
如果加载也使用 协作解析,则在分摊加载之后和解析之前,Vertica 会将部分拆分成块以进行并行加载。
实施分摊加载
要实施分摊加载,请在源、解析器及其工厂中执行下列操作。
在您的 SourceFactory
子类中:
-
实施
isSourceApportionable()
并返回true
。 -
实施
plan()
,以确定部分大小,指定部分,以及将各个部分分配给执行节点。要将多个部分分配给特定的执行程序,请使用计划上下文 (PlanContext::getWriter()
) 上的参数编写器来传递信息。 -
实施
prepareUDSources()
。Vertica 将使用由工厂创建的计划上下文对每个执行节点调用此方法。此方法将返回UDSource
实例,以用于该节点的已分配部分。 -
如果源可以利用并行度,您可以实施
getDesiredThreads()
为每个源请求多个线程。有关此方法的详细信息,请参阅 SourceFactory 类。
在 UDSource
子类中,与对任何其他源一样使用已分配的部分实施 process()
。可以使用 getPortion()
检索此部分。
在您的 ParserFactory
子类中:
-
实施
isParserApportionable()
并返回true
。 -
如果解析器使用支持分摊加载的
UDChunker
,请实施isChunkerApportionable()
。
在您的 UDParser
子类中:
-
编写
UDParser
子类,使其对各个部分而非整个源执行操作。您可以通过处理流状态PORTION_START
和PORTION_END
或使用ContinuousUDParser
API 来完成该编写。解析器必须扫描该部分的开头,查找该部分之后的第一个记录边界并解析至从该部分开始的最后一个记录的结尾。请注意,此行为可能会要求解析器在该部分的结尾之后继续读取。 -
对某个部分不包含任何记录这一特殊情况进行如下处理:返回但不写入任何输出。
在 UDChunker
子类中,实施 alignPortion()
。请参阅对齐部分。
示例
SDK 在 ApportionLoadFunctions
目录中提供了分摊加载的 C++ 示例:
-
FilePortionSource
是UDSource
的子类。 -
DelimFilePortionParser
是ContinuousUDParser
的子类。
将这些类一起使用。您还可以将 FilePortionSource
与内置的分隔解析器结合使用。
以下示例显示了如何加载库并在数据库中创建函数:
=> CREATE LIBRARY FilePortionSourceLib as '/home/dbadmin/FP.so';
=> CREATE LIBRARY DelimFilePortionParserLib as '/home/dbadmin/Delim.so';
=> CREATE SOURCE FilePortionSource AS
LANGUAGE 'C++' NAME 'FilePortionSourceFactory' LIBRARY FilePortionSourceLib;
=> CREATE PARSER DelimFilePortionParser AS
LANGUAGE 'C++' NAME 'DelimFilePortionParserFactory' LIBRARY DelimFilePortionParserLib;
以下示例显示了如何使用源和解析器来加载数据:
=> COPY t WITH SOURCE FilePortionSource(file='g1/*.dat') PARSER DelimFilePortionParser(delimiter = '|',
record_terminator = '~');