vertica用戶自定義擴展開發

開發用戶定義的擴展(UDx)

用戶自定義的擴展(UDx)是包含在外部共享庫中的函數,這些庫是使用Vertica SDK 以C ++,Python,Java或R開發的。外部庫是使用CREATE LIBRARY語句在Vertica目錄中定義的。它們最適合那些難以在SQL中執行的分析操作,或者需要經常執行但速度存在性能瓶頸的的分析操作。

UDx的主要優勢是:

  • 它們可以在可以使用內部功能的任何地方使用。
  • 它們充分利用了Vertica的分佈式計算功能。擴展通常在集羣中的每個節點上並行執行。
  • Vertica處理UDx庫向各個節點的分發。您只需要將庫複製到啓動器節點即可。
  • Vertica可以爲您解決開發分佈式分析代碼的所有複雜問題。您的主要編程任務是讀取數據,進行處理,然後使用Vertica SDK API 將其輸出。

關於開發UDx,需要牢記以下幾點:

  • 可以使用C ++,Python,Java和R等編程語言來開發UDx。(並非所有UDx類型都支持所有語言。)
  • 用Java,Python和R編寫的UDx始終在隔離模式下運行
  • 用C ++開發的UDx可以選擇以非隔離模式運行,這意味着它們可以直接在Vertica數據庫進程中加載和運行。此選項提供最低的開銷和最高的速度。但是,UDx代碼中的任何錯誤都可能導致數據庫不穩定。在將它們部署到實時環境中之前,您必須徹底測試打算以非隔離模式運行的所有UDx。考慮一下,無限制地運行C ++ UDx所帶來的性能提升是否值得因爲有缺陷的UDx可能引起的潛在數據庫不穩定。
  • 因爲UDx在Vertica羣集上運行,所以它可以從數據庫進程中節省處理器時間和內存。消耗大量計算資源的UDx會對數據庫性能產生負面影響。
結構體

每種UDx類型都包含兩個類。
主類完成主要工作(轉換,聚合等)。該類通常至少具有三種方法:一種用於設置,一種用於清除(釋放保留的資源),一種用於實際工作。有時會定義其他方法。
主處理方法接收ServerInterface類的實例作爲參數。底層的Vertica SDK代碼使用此對象來回調用Vertica進程,例如分配內存。您可以使用此類在UDx執行期間寫入服務器日誌。

第二個類是單例工廠。它定義了一個生成主類實例的方法,並且可能定義了其他方法來管理參數。

實施UDx時,必須將這兩個類都子類化。

搭建開發環境

可選擇在vertica非生產環境上進行開發測試UDx。

C++開發環境要求

至少,您需要在開發計算機上安裝以下內容:

  • g ++及其相關的工具鏈,例如ld。
  • Vertica SDK文件。
Java開發環境要求
  • 與數據庫主機上已安裝的Java版本匹配的Java開發工具包(JDK)版本。
  • Vertica SDK文件。
Python開發環境要求

Vertica不需要任何其他文件或軟件包。您可以使用文本編輯器在任何系統上開發Python UDx。

R開發環境要求

Vertica不需要任何其他文件或軟件包。您可以使用文本編輯器在任何系統上開發R UDx。

UDx示例

在/opt/vertica/sdk/example目錄下,有各種語言的UDx示例,可進行參考編寫。

使用C++ SDK進行開發

可以以C++ 11編寫各種類型的隔離或非隔離的UDx,其開發流程如下:
在這裏插入圖片描述

設置C++ SDK

SDK文件位於Vertica服務器根目錄(通常爲/opt/vertica/sdk)下的include子目錄中,其中包含編譯UDx庫所需的頭和源文件。

編譯UDx時,include目錄中有兩個文件:

  • Vertica.h是SDK的主要頭文件。您的UDx代碼需要包含此文件才能找到SDK的定義。
  • Vertica.cpp 包含需要編譯到UDx庫中的支持代碼。

大部分Vertica SDK API在VerticaUDx.h頭文件中定義(該文件包含在頭Vertica.h文件中)。

編譯C++庫

GNU g ++是唯一支持編譯UDx庫的編譯器。應始終在與Vertica羣集上使用的版本相同的的Linux上編譯UDx代碼。

編譯庫時,必須始終:

  • 將-shared和-fPIC標誌傳遞給鏈接器。最簡單的方法是在編譯和鏈接庫時將這些標誌傳遞給g ++。
  • 當不使用宏參數時,使用-Wno-unused-value標誌禁止顯示警告。如果不使用此標誌,則可能會收到 "逗號的左操作數無效"的警告。
  • 編譯sdk/include/Vertica.cpp並將其鏈接到您的庫中。該文件包含支持例程,可幫助您的UDx與Vertica通信。最簡單的方法是將其包含在g++命令中以編譯您的庫。Vertica將此文件作爲C++源而不是庫提供,以限制庫兼容性問題。
  • 使用g++ 的-I標誌,將Vertica SDK include目錄添加到搜索路徑中。

示例:將MyUDx.cpp編譯生成MyUDx.so文件

g++ -I /opt/vertica/sdk/include -Wall -shared -Wno-unused-value -fPIC -o MyUDx.so MyUDx.cpp /opt/vertica/sdk/include/Vertica.cpp
C++ SDK的數據類型

可參考:https://www.vertica.com/docs/9.1.x/HTML/CppSDK/annotated.htm


以UDSF(自定義標量函數) 爲例,介紹使用C++開發UDx的開發的流程。

總述
UDSF類概述

您可以通過繼承Vertica的SDK定義的ScalarFunction和ScalarFunctionFactory兩個類創建UDSF 。

ScalarFunction

該ScalarFunction類是UDSF的核心。您的子類必須定義processBlock()執行標量運算的方法。它可以定義設置和刪除功能的函數。

該processBlock()方法執行您希望UDSF執行的所有處理。當用戶在SQL語句中調用函數時,Vertica會將函數參數中的數據捆綁在一起,並將其傳遞給processBlock()。

processBlock()方法的輸入和輸出由BlockReader和BlockWriter類的對象提供。它們定義了用於讀取UDSF的輸入數據和寫入輸出數據的方法。

開發UDSF的大部分工作是寫作processBlock()。這就是函數中所有處理的地方。您的UDSF應該遵循以下基本模式:

  • BlockReader使用特定於數據類型的方法從對象中讀取一組參數。
  • 以某種方式處理數據。
  • 使用BlockWriter類的特定於數據類型的方法之一輸出結果值。
  • 通過調用BlockWriter.next()和BlockReader.next()前進到輸出和輸入的下一行。

這個過程一直持續到沒有更多的數據行要讀取(BlockReader.next()返回false)爲止。

您必須確保processBlock()讀取其輸入中的所有行,併爲每行輸出一個值。否則可能會損壞Vertica讀取以獲取UDSF輸出的數據結構。

ScalarFunctionFactory

該ScalarFunctionFactory類告訴Vertica有關UDSF的元數據:它的參數和數據類型數量,以及其返回值的數據類型。它還實例化的子類ScalarFunction。

必須在ScalarFunctionFactory子類中實現以下方法:

  • createScalarFunction()實例化一個ScalarFunction子類。
  • getPrototype()告訴Vertica UDSF的參數和返回類型。除了一個ServerInterface對象外,此方法還獲得兩個ColumnTypes對象。該函數所需要做的就是在這兩個對象上調用類函數以構建參數列表和單個返回值類型。
  • 如果您的函數返回列大小(長度可以變化的返回數據類型,例如VARCHAR)或需要精度的值,則必須實現 getReturnType()。Vertica調用此方法以查找結果每一行中返回的數據的長度或精度。此方法的返回值取決於您的processBlock()方法返回的數據類型。getReturnType()方法的輸入是一個SizedColumnTypes對象,其中包含輸入參數類型及其長度。該對象將被傳遞到您的processBlock()函數的實例。您的實現getReturnType()必須從此輸入中提取數據類型和長度,並確定輸出行的長度或精度。然後,它將此信息保存在SizedColumnTypes該類的另一個實例中。
  • 如果您的函數接受其他參數,需要定義一個獲取參數的函數getParameterType。它從一個對象獲取其參數值,該對象可以從傳遞給您的處理方法的對象中獲得。

定義工廠類後,您需要調用RegisterFactory宏。該宏實例化了工廠類的成員,因此Vertica可以與其進行交互並提取其中包含的有關UDSF的元數據。

C++ UDSF API

可參考:ScalarFunction and ScalarFunctionFactory C++ Interface

示例

以下示例顯示了一個非常基本的子類,ScalarFunction名爲Add2ints。顧名思義,它將兩個整數相加,返回一個整數結果。它還演示了包括主Vertica SDK頭文件(Vertica.h)以及如何使用Vertica名稱空間。儘管不是必需的,但使用名稱空間可以使您不必在每個Vertica SDK類引用之前添加前綴Vertica::。

ScalarFunction :

// Include the top-level Vertica SDK file
#include "Vertica.h"
// Using the Vertica namespace means we don't have to prefix all
// class references with Vertica::
using namespace Vertica;
/* 
 * ScalarFunction implementation for a UDSF that adds 
 * two numbers together. 
 */
class Add2Ints : public ScalarFunction
 {
    public:
   /*
    * This function does all of the actual processing for the UDF.
    * In this case, it simply reads two integer values and returns
    * their sum.
    *
    * The inputs are retrieved via arg_reader
    * The outputs are returned via arg_writer
    */
    virtual void processBlock(ServerInterface &srvInterface,
                            BlockReader &arg_reader,
                            BlockWriter &res_writer)
    {
    // While we have input to process
        do {
            // Read the two integer input parameters by calling the
            // BlockReader.getIntRef class function
            const vint a = arg_reader.getIntRef(0);
            const vint b = arg_reader.getIntRef(1);
            // Call BlockWriter.setInt to store the output value, which is the
            //  two input values added together
            res_writer.setInt(a+b);
            // Finish writing the row, and advance to the next output row
            res_writer.next();
            // Continue looping until there are no more input rows
        } while (arg_reader.next());
  }
};

ScalarFunctionFactory :

/*
 * This class provides metadata about the ScalarFunction class, and
 * also instantiates a member of that class when needed.
 */
class Add2IntsFactory : public ScalarFunctionFactory
{
	// return an instance of Add2Ints to perform the actual addition.
	virtual ScalarFunction *createScalarFunction(ServerInterface &interface)
	{
		// Calls the vt_createFuncObj to create the new Add2Ints class instance.
		return vt_createFuncObj(interface.allocator, Add2Ints);
	}
	// This function returns the description of the input and outputs of the
	// Add2Ints class's processBlock function.  It stores this information in
	// two ColumnTypes objects, one for the input parameters, and one for
	// the return value.
	virtual void getPrototype(ServerInterface &interface,
	ColumnTypes &argTypes,
	ColumnTypes &returnType)
	{
		// Takes two ints as inputs, so add ints to the argTypes object
		argTypes.addInt();
		argTypes.addInt();
		// returns a single int, so add a single int to the returnType object.
		// Note that ScalarFunctions *always* return a single value.
		returnType.addInt();
	}
};

RegisterFactory宏:

使用RegisterFactory宏註冊一個ScalarFunctionFactory子類。該宏實例化工廠類,並使其中包含的元數據可供Vertica訪問。要調用此宏,請將其傳遞給您的工廠類的名稱。

RegisterFactory(Add2IntsFactory);

對於獲取其他參數的示例,可參考:
C++ Example: Defining Parameters
對於獲取返回列長度或精度的getReturnType()函數,UDSF處並沒有示例,可參考UDTF處的示例:關注getReturnType的定義

之後就可以編譯,創建庫,創建函數來使用該UDx。

本文主要參考:Developing User-Defined Extensions (UDxs)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章