1. 程式人生 > >hive開發UDF及使用

hive開發UDF及使用

 最近有個資料探勘的需求,要求統計所給經緯度附近n公里某些事物的數量。涉及到地球兩點間的距離計算,需要寫UDF進行計算。

一、UDF編寫

 根據經緯度計算兩點間的距離,網上有很多計算方法,試了幾個,發現這篇部落格的方法計算的精度差比較小,他的分析方法也很詳細,最終採用此方法。

import com.ai.hive.udf.topdomain.StringUtil;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
import org.apache.log4j.Logger;

/**
 * 功能:根據兩地經緯度計算兩點之間的距離
 * create temporary function LocDistanceCalUDF as 'com.ai.hive.udf.util.LocDistanceCalUDF';
 * @author olicity
 */
public class LocDistanceCalUDF extends UDF{
    private static Logger log = Logger.getLogger(LocDistanceCalUDF.class);

    private Text nullText = new Text("");
    /**
    *根據經緯度計算地球兩點間的距離
    */
    private static double distanceCal(double lng1, double lat1,double lng2,double lat2){
        double dx = lng1 - lng2;// 經度差值
        double dy= lat1 - lat2;// 緯度差值
        double b = (lat1 + lat2) / 2.0;// 平均緯度
        double Lx = Math.toRadians(dx)*6367000.0*Math.cos(Math.toRadians(b));// 東西距離
        double Ly = 6367000.0*Math.toRadians(dy);// 南北距離
        return Math.sqrt(Lx*Lx+Ly*Ly);// 用平面的矩形對角距離公式計算總距離(米)
    }
    /**
    *重寫evaluate方法
    */
    public Text evaluate(Text longitudeText1, Text latitudeText1,Text longitudeText2, Text latitudeText2){
        try{
            if(longitudeText1==null || latitudeText1==null || longitudeText2==null || latitudeText2==null){
                return nullText;
            }
            if(StringUtil.isEmpty(longitudeText1.toString()) || StringUtil.isEmpty(latitudeText1.toString()) || StringUtil.isEmpty(longitudeText2.toString()) || StringUtil.isEmpty(latitudeText2.toString())){
                return nullText;
            }
            double lng1 = Double.valueOf(longitudeText1.toString());
            double lat1 = Double.valueOf(latitudeText1.toString());
            double lng2 = Double.valueOf(longitudeText2.toString());
            double lat2 = Double.valueOf(latitudeText2.toString());

            double dis = distanceCal(lng1,lat1,lng2,lat2);
            return new Text(String.valueOf(dis));
        }catch (Exception e){
            return nullText;
        }

    }
    /**
    *重寫evaluate方法
    */
    public Text evaluate(Text locationA,Text locationB){
        try{
            if (locationA==null||locationB==null){
                return nullText;
            }
            if(StringUtil.isEmpty(locationA.toString()) || StringUtil.isEmpty(locationB.toString())){
                return nullText;
            }
            String locationA2String  = locationA.toString();
            String locationB2String  = locationB.toString();
            double lng1 = Double.valueOf(locationA2String.split(",")[0]);
            double lat1 = Double.valueOf(locationA2String.split(",")[1]);
            double lng2 = Double.valueOf(locationB2String.split(",")[0]);
            double lat2 = Double.valueOf(locationB2String.split(",")[1]);

            double dis = distanceCal(lng1,lat1,lng2,lat2);
            return new Text(String.valueOf(dis));
        }catch(Exception e){
            return nullText;
        }
    }

}

 UDF類要繼承org.apache.hadoop.hive.ql.exec.UDF類,類中要實現evaluate。 當我們在hive中使用自定義的UDF的時候,hive會呼叫類中的evaluate方法來實現特定的功能。

二、UDF匯入

1.jar包上傳

 右鍵類名,Copy reference,複製此類全路徑得到:com.ai.hive.udf.util.LocDistanceCalUDF。將寫完的類打成jar包上傳到伺服器。路徑如:/user/olicity/hive/UDF

2.jar包引入classpath變數中

進入hive,引入jar包,執行命令

add jar /user/olicity/hive/UDF/udf.jar;

檢視匯入的jar包

list jars;

3.建立函式

建立一個名為LocDistanceCalUDF的臨時函式,關聯該jar包

create temporary function LocDistanceCalUDF as 'com.ai.hive.udf.util.LocDistanceCalUDF';

檢視建立的函式

show functions;

4.注意

 上述方法僅限於當前會話生效,如需要新增一個永久的函式對應的永久的路徑,則

create function locUDF.LocDistanceCalUDF 
  as 'com.ai.hive.udf.util.LocDistanceCalUDF' 
  using jar 'hdfs://hdfs路徑/udf.jar';
use LocDistanceCalUDF;

需要將jar包放到hdfs上,然後建立函式關聯路徑即可。 另外還看到過另一種方法,配置hive-site.xml檔案中的hive.aux.jars.path

配置參考如下:
   <property>
       <name>hive.aux.jars.path</name>
       <value>file:///home/hdfs/fangjs/DefTextInputFormat.jar,file:///jarpath/test.jar</value>
   </property>

三、UDF使用

 準備工作已經就緒,可以開始查表了。emmmmm,就簡單的倆表查吧,表結構和表資料就不展示了,示例表也就不建了,所給經緯度表叫A表,需要查詢的表叫B表,臨時中間表叫c表,經緯度的表中欄位定義是loc,距離就算2公里吧。

create table C
as
select B.* from B join A where (LocDistanceCalUDF(A.loc,B.loc)<=2000);

OK.

四、總結

 關於sql語句還是需要再多加練習,尤其是多表聯查。Hadoop之路任重而道遠。