相對於使用MapReduce或者Spark Application的方式進行數據分析,使用Hive SQL或Spark SQL能爲咱們省去很多的代碼工做量,而Hive SQL或Spark SQL自己內置的各種UDF也爲咱們的數據處理提供了很多便利的工具,當這些內置的UDF不能知足於咱們的須要時,Hive SQL或Spark SQL還爲咱們提供了自定義UDF的相關接口,方便咱們根據本身的需求進行擴展。
在Hive的世界裏使用自定義UDF的過程是比較複雜的。咱們須要根據需求使用Java語言開發相應的UDF(UDAF、UDTF),而後將UDF的代碼及其依賴編譯打包爲Jar,使用方法有兩種:
(1)臨時函數
在一次會話(Session)中使用以下語句建立臨時函數:
ADD JAR /run/jar/udf_test.jar;
CREATE TEMPORARY FUNCTION my_add AS 'com.hive.udf.Add';
這種方式有一個缺點:每一次會話過程當中使用函數時都須要建立,並且僅在當前會話中有效。
(2)永久函數
這個特性須要高版本的Hive支持,它的好處是能夠將UDF Jar存放至HDFS,函數僅須要建立一次便可以永久使用,以下:
CREATE FUNCTION func.ipToLocationBySina AS 'com.sina.dip.hive.function.IPToLocationBySina' USING JAR 'hdfs://dip.cdh5.dev:8020/user/hdfs/func/location.jar';
雖然永久函數相對於臨時函數有必定優點,但Java語言的開發門檻很大程度上妨礙了UDF在實際數據分析過程當中使用,畢竟咱們的數據分析師多數是以Python、SQL爲主要分析工具的,每一次UDF的開發都須要工程師的參與,開發效率與應用效果都是否是很好(可能須要頻繁更新UDF的問題),PySpark的出現確很好地解決了這個問題:它能夠很是方便地將一個普通的Python函數註冊爲一個UDF。
爲了說明如何在Spark(Hive) SQL中的使用Python UDF,咱們首先模擬一張數據表,爲了簡單起見,該表僅有一行一列數據:
咱們模擬了一張數據表temp_table,該表僅有一列,其中列名稱爲col,列類型爲字符串且不容許包含Null,輸出結果:
咱們在表temp_table的基礎之上演示UDF的使用方法:
首先咱們定義一個普通的Python函數:func_string,爲了簡單起見它沒有任何參數,僅僅返回一個簡單的字符串;
而後咱們經過HiveContext registerFunction便可以將函數func_string註冊爲UDF,registerFunction接收兩個參數:UDF名稱、UDF關聯的Python函數;
最後咱們能夠在Spark(Hive) SQL中使用這個UDF,輸出結果:
咱們須要注意的是,HiveContext registerFunction實際上有三個參數:
name:UDF名稱;
f:UDF關聯的Python函數;
returnType:UDF(Python函數)返回值類型,默認爲StringType()。
上述示例中由於咱們的UDF函數的返回值類型爲字符串,所以使用Hive registerFunction註冊UDF時省略了參數returnType,即returnType默認值爲StringType(),若是UDF(Python函數)的返回值類型不爲字符串,則須要顯式爲其指定returnType。
咱們以類型IntegerType、ArrayType、StructType、MapType爲例演示須要顯式指定returnType的狀況。
(1)IntegerType
(2)ArrayType
注意:ArrayType(數組)必須確保元素類型的一致性,如指定UDF返回值類型爲ArrayType(IntegerType()),則函數func_array的返回值類型必須爲list或tuple,其中的元素類型必須爲int。
(3)StructType
注意:StructType必須確保函數的返回值類型爲tuple,並且使用HiveContext registerFunction註冊UDF時須要依次爲其中的元素指定名稱各種型,如上述示例中每個元素的名稱爲first,類型爲IntegerType;第二個元素的名稱爲second,類型爲FloatType;第三個元素的名稱爲third,類型爲StringType。
(4)MapType
注意:MapType必須確保函數的返回值類型爲dict,並且全部的「key」應保持類型一致,「value」也就保持類型一致。