1. 程式人生 > >hive udf開發超詳細手把手教程(有些過時了)

hive udf開發超詳細手把手教程(有些過時了)

mvn install 直接打包

maven打包
上面程式碼測試通過以後,然後用maven打成jar包。如果是老司機,自然知道怎麼做。如果是新司機,我偷偷告訴大家,eclipse裡在專案上右擊,選擇 run as,然後maven install,maven就開始幫你打包了。如果是第一次,maven還會幫你把依賴的jar下載到本地倉庫。打包完了以後,你就可以看到target目錄下面出現了一個jar包,festival-1.0.jar。OK,這就是我們需要的jar包。
--------------------- 
作者:bitcarmanlee 
來源:CSDN 
原文:https://blog.csdn.net/bitcarmanlee/article/details/51249260 
版權宣告:本文為博主原創文章,轉載請附上博文連結!

 

hive udf開發超詳細手把手教程

2016年04月26日 11:46:34 bitcarmanlee 閱讀數:29652 標籤: hiveudf開發教程 更多

個人分類: hive

版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/bitcarmanlee/article/details/51249260

關於hive的udf介紹,就不多囉嗦了。網上的教程一抓一大把,也可以上apache的官網去查閱相關資料,我就省了翻譯的時間了。重點給大家帶來乾貨,手把手教會你怎樣開發一個udf函式,已經如何部署到伺服器上的hive環境中執行。用最簡單的話來說,就是教大家怎麼讓自己開發的udf跑起來。。。

專案需求

做資料探勘專案中,常見的需求之一就是分析節假日訂單跟平時訂單的區別。於是,我們需要統計節假日訂單的分佈情況。但是hive中顯然沒有內建,也不可能內建此函式,因為每年的節假日都是變得嘛。於是,我們就需要自己開發一個udf來滿足需求了。

配置檔案

考慮到每年的節假日其實並不多,也就那麼二十多天,於是採用配置檔案的方式,直接將節假日寫死在配置檔案中。如果需要新增,改配置檔案就行。畢竟一年也就這麼二十多天,工作量並不大。。。 
自己寫的配置檔案如下:

20140101=元旦
20140131=春節
20140201=春節
20140202=春節
20140203=春節
20140204=春節
20140205=春節
20140206=春節
20140405=清明節
20140406=清明節
20140407=清明節
20140501=五一勞動節
20140502=五一勞動節
20140503=五一勞動節
20140531=端午節
20140601=端午節
20140602=端午節
20140906=中秋節
20140907=中秋節
20140908=中秋節
20141001=國慶節
20141002=國慶節
20141003=國慶節
20141004=國慶節
20141005=國慶節
20141006=國慶節
20141007=國慶節

20150101=元旦
20150102=元旦
20150103=元旦
20150218=春節
20150219=春節
20150220=春節
20150221=春節
20150222=春節
20150223=春節
20150224=春節
20150404=清明節
20150405=清明節
20150406=清明節
20150501=五一勞動節
20150502=五一勞動節
20150503=五一勞動節
20150620=端午節
20150621=端午節
20150622=端午節
20150926=中秋節
20150927=中秋節
20151001=國慶節
20151002=國慶節
20151003=國慶節
20151004=國慶節
20151005=國慶節
20151006=國慶節
20151007=國慶節

20160101=元旦
20160102=元旦
20160103=元旦
20160207=春節
20160208=春節
20160209=春節
20160210=春節
20160211=春節
20160212=春節
20160213=春節
20160402=清明節
20160403=清明節
20160404=清明節
20160430=五一勞動節
20160501=五一勞動節
20160502=五一勞動節
20160609=端午節
20160610=端午節
20160611=端午節
20160915=中秋節
20160916=中秋節
20160917=中秋節
20161001=國慶節
20161002=國慶節
20161003=國慶節
20161004=國慶節
20161005=國慶節
20161006=國慶節
20161007=國慶節
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86

以上包含有2014,2015,2016三年的假期。如果想增加,繼續往此檔案裡新增就是。。。

新建maven專案

現在的java專案必須是用maven管理,方便又實用,誰用誰知道,不多解釋。maven專案自然只需要知道pom.xml即可。pom檔案如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>udf.leilei.elong.com</groupId>
  <artifactId>festival</artifactId>
  <version>1.0</version>

  <name>hive</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
        <groupId>org.apache.hive</groupId>
        <artifactId>hive-exec</artifactId>
        <version>0.13.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-common</artifactId>
        <version>2.5.0</version>
    </dependency>
  </dependencies>
  <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.2</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
 </build>
</project>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

提醒對maven不是很熟的同學們:因為我們專案裡有依賴的jar包,所以必須加上 maven-shade-plugin 外掛。當然你用 maven-assembly-plugin 也是沒有問題的。如果你對maven不是那麼熟悉,別管了,先粘過去,跑起來再說吧。。。

UDF具體程式碼開發

package festival;

import java.io.InputStreamReader;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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

public class FestivalType extends UDF{

    private HashMap<String,String> festivalMap = new HashMap<String, String>();

    public FestivalType() throws Exception {
        InputStreamReader propFile = new InputStreamReader(getClass().getClassLoader().getResourceAsStream("festival_date.properties"), "UTF-8");
        Properties prop = new Properties();
        prop.load(propFile);
        for(Object key:prop.keySet()) {
            festivalMap.put(key.toString(), prop.getProperty(key.toString()));
        }
    }

    //解決double轉string的科學計數法的問題
    public String double_to_string(double dou) {
        Double dou_obj = new Double(dou);
        NumberFormat nf = NumberFormat.getInstance();
        nf.setGroupingUsed(false);
        String dou_str = nf.format(dou_obj);
        return dou_str;
    }

    public String evaluate(double date_dou) {
        String date_str = this.double_to_string(date_dou);
        return evaluate(date_str);
    }

    public int evaluate(double date_dou,String flag) {
        String date_str = this.double_to_string(date_dou);
        return evaluate(date_str,flag);
    }

    public String evaluate(String date_str) {
        if (! this.match_date(date_str).equals("null")) {
            date_str = this.match_date(date_str);
            return festivalMap.get(date_str) == null ? "null" : festivalMap.get(date_str);
        } else {
            return "null";
        }
    }

    public int evaluate(String date_str, String flag) {
        if (flag.equals("count") && ! this.match_date(date_str).equals("null")) {
            date_str = this.match_date(date_str);
            return festivalMap.get(date_str) == null ? 0 :1;
        } else {
            return 0;
        }
    }

    public String match_date(String date_str) {
        //匹配20160101這種日期格式
        Pattern pat_common = Pattern.compile("\\d{8}");
        Matcher mat_common = pat_common.matcher(date_str);

        //匹配2016-01-01這種日期格式
        Pattern pat_strike = Pattern.compile("\\d{4}-\\d{2}-\\d{2}");
        Matcher mat_strike = pat_strike.matcher(date_str);

        //匹配 2016-01-01 10:35:46 這種日期格式
        Pattern pat_colon = Pattern.compile("\\d{4}-\\d{2}-\\d{2}(\\s)+\\d{2}:\\d{2}:\\d{2}");
        Matcher mat_colon = pat_colon.matcher(date_str);

        if (mat_colon.find()) {
            return date_str.replace("-", "").substring(0, 8);
        } else if(mat_strike.find()) {
            return date_str.replace("-", "");
        } else if (mat_common.find()) { 
            return date_str;
        } else {
            return "null";
        }
    }

    //測試的main方法
    public static void main(String[] args) throws Exception{
        FestivalType fes = new FestivalType();
        String date_str = "20150101";
        System.out.println(fes.evaluate(date_str));

        String date_str1 = "20160101";
        String result = fes.evaluate(date_str1);
        System.out.println("result is:" + result);

        double date_dou = 20160101;
        int result_dou = fes.evaluate(date_dou,"count");
        System.out.println(result_dou);
        System.out.println(date_dou);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102

將之前的配置檔案放在src/main/resources下面,然後執行此程式碼,輸出如下:

節日型別是:元旦
節日天數:1
  • 1
  • 2

因為我們這篇文章的目的是讓udf以最快的速度跑起來,所以udf具體實現細節以及原理就不多寫了。大家記住下面一句話就可以:繼承UDF類,重寫evaluate方法,就可以了。

maven打包

上面程式碼測試通過以後,然後用maven打成jar包。如果是老司機,自然知道怎麼做。如果是新司機,我偷偷告訴大家,eclipse裡在專案上右擊,選擇 run as,然後maven install,maven就開始幫你打包了。如果是第一次,maven還會幫你把依賴的jar下載到本地倉庫。打包完了以後,你就可以看到target目錄下面出現了一個jar包,festival-1.0.jar。OK,這就是我們需要的jar包。

將jar包上傳

接下來,我們將前面的jar包上傳到伺服器上的任何一個位置。比如我就是用scp命令,不多說。

使用udf查詢

#!/bin/bash

hive -e "add jar /home/xxx/lei.wang/festival-1.0.jar;
         create temporary function festival as 'festival.FestivalType';
         set mapred.reduce.tasks = 10;
         select cast(a.create_date_wid as int) create_date_wid,sum(a.festival_order_num) from
            (select create_date_wid,festival(create_date_wid,'count') as festival_order_num from ora_dw_rewrite.olap_b_dw_hotelorder_f
                where create_date_wid>=20160101 and create_date_wid<=20160406)a
            where a.festival_order_num > 0
                group by a.create_date_wid
                    order by create_date_wid;"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

為了達到快速上手的目的,直接上程式碼。

add jar /home/xxx/lei.wang/festival-1.0.jar;
  • 1

這一行是將jar包加進來,後面是你前面上傳的路徑地址

create temporary function festival as 'festival.FestivalType';
  • 1

建立一個臨時函式,用來查詢,festival.FestivalType是package名+類名,無需多解釋。 
後面的查詢語句,也不多解釋了。有點sql基礎的同學,應該都能看明白。

最後查詢結果

..........
Stage-Stage-1: Map: 62  Reduce: 10   Cumulative CPU: 1547.44 sec   HDFS Read: 29040366442 HDFS Write: 1298 SUCCESS
Stage-Stage-2: Map: 8  Reduce: 1   Cumulative CPU: 23.88 sec   HDFS Read: 4608 HDFS Write: 205 SUCCESS
Total MapReduce CPU Time Spent: 26 minutes 11 seconds 320 msec
OK
20160101    xxx
20160102    xxx
20160103    xxx
20160207    xxx
20160208    xxx
20160209    xxx
20160210    xxx
20160211    xxx
20160212    xxx
20160213    xxx
20160402    xxx
20160403    xxx
20160404    xxx
Time taken: 159.805 seconds, Fetched: 13 row(s)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

後面的具體數值是公司機密,不能透露,嘻嘻。 
稍微注意一點是cast(a.create_date_wid as int),這裡因為create_date_wid存的是double型別,如果不做轉化的話,會顯示成科學計數法,2.0150326E7這種形式。所以為了好看一些,將其強轉成int型別。 
還有就是這種add的方式的有效期是session級別,就是在此seession有效,session關閉以後就無效。所以如果要將其固話的話,可以加到hive的classpath裡頭。這樣hive啟動的時候,就將其加到classpath裡了,無需再手動add jar。

結束語

至此,一個完整的hive udf開發過程就結束了。當然還可以開發UDAF,限於時間有限,就不再詳細介紹。歡迎有搞資料,演算法的志同道合的同學們一起研究討論

QQ:535352700 
EMAIL:[email protected]