Hive筆記十:自定義函數UDF和UDAF

Hive進行UDF開發十分簡單,此處所說UDF爲Temporary的function,所以需要hive版本在0.4.0以上纔可以。

一、背景:Hive是基於Hadoop中的MapReduce,提供HQL查詢的數據倉庫。Hive是一個很開放的系統,很多內容都支持用戶定製,包括:

a)文件格式:Text File,Sequence File

b)內存中的數據格式: Java Integer/String, Hadoop IntWritable/Text

c)用戶提供的 map/reduce腳本:不管什麼語言,利用 stdin/stdout 傳輸數據

d)用戶自定義函數: Substr, Trim, 1 – 1

e)用戶自定義聚合函數: Sum, Average…… n – 1

2、定義:UDF(User-Defined-Function),用戶自定義函數對數據進行處理。

二、用法

1、UDF函數可以直接應用於select語句,對查詢結構做格式化處理後,再輸出內容。

2、編寫UDF函數的時候需要注意一下幾點:

a)自定義UDF需要繼承org.apache.hadoop.hive.ql.UDF。

b)需要實現evaluate函數。

cevaluate函數支持重載。


當Hive提供的內置函數無法滿足你的業務處理需要時,此時就可以考慮使用用戶自定義函數(UDF:user-defined function)。

Hive目前只支持用java語言書寫自定義函數。如果需要採用其他語言,比如Python,可以考慮上一節提到的transform語法來實現。

Hive支持三種自定義函數,我們逐個講解。

UDF

這是普通的用戶自定義函數。接受單行輸入,併產生單行輸出。

編寫java代碼如下:

package com.oserp.hiveudf;

 

import org.apache.hadoop.hive.ql.exec.UDF;

import org.apache.hadoop.io.Text;

 

public classPassExam extendsUDF {

   

    publicText evaluate(Integer score)

    {

        Text result = new Text();

       

        if(score < 60)

            result.set("Failed");

        else

            result.set("Pass");

       

        return result;    

    }

}

然後,打包成.jar文件,比如hiveudf.jar。

執行以下語句:

add jar /home/user/hadoop_jar/hiveudf.jar;

create temporary function pass_scorecom.oserp.hiveudf.PassExam;

select stuNo,pass_score(score) from student;

輸出結果爲:

N0101      Pass

N0102      Failed

N0201      Pass

N0103      Pass

N0302      Pass

N0202      Pass

N0203      Pass

N0301      Failed

N0306      Pass

第一個語句註冊jar文件;第二個語句爲自定義函數取別名;第三個語句調用自定義函數。

Java代碼中,自定義函數的類繼承自UDF類,且提供了一個evaluate方法。這個方法接受一個整數值作爲參數,並返回字符串。結構十分明瞭。其中的evaluate方法並沒有作爲interface提供,因爲實際使用時,函數的參數個數及類型是多變的。

以上UDF名稱是不區分大小寫的,比如調用時寫成PASS_SCORE也是可以的(因爲它是hive中的別名,不是java類名)。

 

使用完成後,可調用以下語句刪除函數別名:

Drop temporary function pass_score;

UDAF

用戶定義聚集函數(User-defined aggregate function)。接受多行輸入,併產生單行輸出。比如MAX,COUNT函數。

編寫以下Java代碼:

packagecom.oserp.hiveudf;

 

importorg.apache.hadoop.hive.ql.exec.UDAF;

importorg.apache.hadoop.hive.ql.exec.UDAFEvaluator;

importorg.apache.hadoop.hive.serde2.io.DoubleWritable;

importorg.apache.hadoop.io.IntWritable;

 

publicclass HiveAvgextends UDAF { 

   

    public staticclass AvgEvaluate implements UDAFEvaluator

    {

        public staticclass PartialResult

        {

            public intcount;

            public doubletotal;     

           

            public PartialResult()

            {

                count = 0;

                total = 0;

            }

        }

       

        private PartialResultpartialResult;  

 

        @Override

        public voidinit() {

            partialResult new PartialResult();

        }

       

        public booleaniterate(IntWritable value)

        {              

            // 此處一定要判斷partialResult是否爲空,否則會報錯

            // 原因就是init函數只會被調用一遍,不會爲每個部分聚集操作去做初始化

            //此處如果不加判斷就會出錯

            if (partialResult==null)

            {

                partialResult =new PartialResult();

            }

           

            if (value !=null)

            {

                partialResult.total =partialResult.total +value.get();

                partialResult.count=partialResult.count + 1;

            }

           

            return true;          

        }

       

        public PartialResult terminatePartial()

        {

            returnpartialResult;

        }

       

        public booleanmerge(PartialResult other)

        {  

            partialResult.total=partialResult.total + other.total;

            partialResult.count=partialResult.count + other.count;

           

            return true;

        }

       

        public DoubleWritable terminate()

        {

            return newDoubleWritable(partialResult.total /partialResult.count);

        }

    }

}

然後打包成jar文件,比如hiveudf.jar。

執行以下語句:

add jar/home/user/hadoop_jar/hiveudf.jar; 

create temporary function avg_udf as'com.oserp.hiveudf.HiveAvg';

select classNo, avg_udf(score) from studentgroup by classNo;  

輸出結果如下:

C01 68.66666666666667

C02 80.66666666666667

C03 73.33333333333333

 

參照以上圖示(來自Hadoop權威教程)我們來看看各個函數:

l  Init在類似於構造函數,用於UDF的初始化。

注意上圖中紅色框中的init函數。在實際運行中,無論hive將記錄集劃分了多少個部分去做(比如上圖中的file1和file2兩個部分),init函數僅被調用一次。所以上圖中的示例是有歧義的。這也是爲什麼上面的代碼中加了特別的註釋來說明。或者換一句話說,init函數中不應該用於初始化部分聚集值相關的邏輯,而應該處理全局的一些數據邏輯。

l  Iterate函數用於聚合。當每一個新的值被聚合時,此函數被調用。

l  TerminatePartial函數在部分聚合完成後被調用。當hive希望得到部分記錄的聚合結果時,此函數被調用。

l  Merge函數用於合併先前得到的部分聚合結果(也可以理解爲分塊記錄的聚合結果)。

l  Terminate返回最終的聚合結果。

 

我們可以看出merge的輸入參數類型和terminatePartial函數的返回值類型必須是一致的。


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