CloudHadoop에서 Hive UDF 실행하기

Suewoon Ryu
NAVER CLOUD PLATFORM
10 min readNov 18, 2020

이 페이지에서는 Hive UDF(User Defined Function, 이하 UDF)를 구현하고, CloudHadoop에서 UDF를 사용하는 방법에 대해 설명합니다. Cloud Hadoop 1.2 클러스터가 이미생성되어 있다고 가정합니다.

Cloud Hadoop 생성하러 가기

UDF?

Hive는 사용자가 작성한 processing code를 붙여서, hive query내에서 실행 할 수 있게 했습니다. built-in function만으로는 원하는 쿼리를 표현하기 어려울 때 사용합니다. 보통 검색로그, 거래내역 등 특정 필드 데이터에 활용할 수 있도록 UDF를 작성하여 사용합니다.

function이 받는 input row, 반환하는 output row 수에 따라 3가지 종류의 UDF로 나눌 수 있습니다. 각 함수의 종류마다 구현해야 하는 interface가 다릅니다.

  • UDF: single row를 input으로 받아서 single row를 output으로 반환하는 함수입니다. ROUND, REPLACE 같은 대부분의 mathematical, string functions이 이런 타입에 해당합니다.
  • UDAF: multiple rows를 input으로 받아서 single row를 output으로 반환하는 함수입니다. COUNT, MAX 같은 aggregate functions이 해당합니다.
  • UDTF: single rows를 input으로 받아서 multiple rows(table)를 ouput으로 반환하는 함수입니다. EXPLODE 같은 함수가 해당합니다.

이번 페이지에서는 가장 간단한 UDF를 작성 할 것이므로org.apache.hadoop.hive.ql.exec.UDF 인터페이스를 구현 합니다.

구현 및 사용

UDF는 java로 구현해야 하며, 다른 프로그래밍 언어를 사용하고자 한다면 user-defined script(map reduce script)를 만들어서 SELECT TRANSFORM 구문을 사용하면 됩니다.

프로젝트 생성

이 페이지에서는 IntelliJ를 사용해서 Gradle Project를 생성합니다. package는 com.naverncp.hive 로 하겠습니다.

Figure1. IntelliJ 에서 New Project 생성하기

프로젝트 root아래 build.gradle 에 아래처럼 dependency 설정을 추가합니다. 여기서는 Cloud Hadoop 1.2에 설치된 컴포넌트와 동일한 버전을 사용하였습니다.

plugins {
id 'java'
}
group 'com.naverncp'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8repositories {
mavenCentral()
maven {
url "<http://conjars.org/repo>"
}
}
dependencies {
compile group: 'org.apache.hadoop', name: 'hadoop-client', version: '2.7.3'
compile group: 'org.apache.hive', name: 'hive-exec', version: '1.2.2'
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.9'
testCompile group: 'junit', name: 'junit', version: '4.12'
}

구현하기

UDF를 구현할 때는, 다음 조건을 만족해야 합니다.

  • UDF는 org.apache.hadoop.hive.ql.exec.UDF를 상속해야 합니다.
  • UDF는 적어도 하나의 evaluate() 메소드를 구현해야 합니다. 함수가 몇개의 argument를 받게 될 지 모르고 argument가 어떤 타입일지 미리 알기 어렵기 때문에 org.apache.hadoop.hive.ql.exec.UDF 인터페이스에 evaluate() 메소드는 정의되어 있지 않기 때문입니다.
// Strip.java
package com.naverncp.hive;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
@Description(
name = "Strip",
value = "returns a stripped text",
extended = "stripping characters from the ends of strings"
)
public class Strip extends UDF {
private Text result = new Text();
public Text evaluate(Text str){
if (str == null){
return null;
}
result.set(StringUtils.strip(str.toString()));
return result;
}
public Text evaluate(Text str, String stripChar){
if (str == null){
return null;
}
result.set(StringUtils.strip(str.toString(), stripChar));
return result;
}
}

위에 클래스에서는 두개의 evaluate 메소드를 구현하였습니다.

  • 첫번째 메소드 - 문자열에서 앞, 뒤 공백을 제거합니다.
  • 두번째 메소드 - 문자열 뒤에서 부터, 지정한 문자를 제거합니다.

이 UDF를 hive에서 사용하려면 먼저 java class를 .jar 로 패키징 해야 합니다. 이 .jar를 hdfs:///user/suewoon 아래에 업로드 하여 사용하도록 하겠습니다.

$ ./gradlew clean
$ ./gradlew build
$ scp -i ~/Downloads/suewoon-home.pem ~/IdeaProjects/hive/build/libs/hive-1.0-SNAPSHOT.jar sshuser@pub-4rrsj.hadoop.ntruss.com:~/
$ ssh -i ~/Downloads/suewoon-home.pem sshuser@pub-4rrsj.hadoop.ntruss.com
[sshuser@e-001-suewoon-0917-hd ~]$ hadoop fs -copyFromLocal hive-1.0-SNAPSHOT.jar /user/suewoon/

사용하기

먼저 임의로 hive table을 하나 생성하도록 하겠습니다.

[sshuser@e-001-suewoon-0917-hd ~]$ echo 'dummy' > /tmp/dummy.txt
[sshuser@e-001-suewoon-0917-hd ~]$ hive -e "CREATE TABLE dummy (value STRING); \\
LOAD DATA LOCAL INPATH '/tmp/dummy.txt' \\
OVERWRITE INTO TABLE dummy"

아래 처럼 Hive CLI 를 시작합니다. edge노드에 HiveServer가 설치되어 있기 때문에 별다른 옵션을 주지 않아도 됩니다.

[sshuser@e-001-suewoon-0917-hd ~]$ hive
20/11/06 16:04:39 WARN conf.HiveConf: HiveConf of name hive.server2.enable.doAs.property does not exist
log4j:WARN No such property [maxFileSize] in org.apache.log4j.DailyRollingFileAppender.
Logging initialized using configuration in file:/etc/hive/2.6.5.0-292/0/hive-log4j.properties
hive>

아래와 같이 metastore에서 함수를 등록하면 됩니다. CREATE FUNCTION 구문으로 이름을 설정합니다.

hive> CREATE FUNCTION strip AS 'com.naverncp.hive.Strip'
> USING JAR 'hdfs:///user/suewoon/hive-1.0-SNAPSHOT.jar';
converting to local hdfs:///user/suewoon/hive-1.0-SNAPSHOT.jar
Added [/tmp/99c3d137-f58e-4fab-8a2a-98361e3e59a1_resources/hive-1.0-SNAPSHOT.jar] to class path
Added resources: [hdfs:///user/suewoon/hive-1.0-SNAPSHOT.jar]
OK
Time taken: 17.786 seconds

또는 metastore에 함수를 영구적으로 저장하지 않고, hive session 동안에만 사용하려면 아래와 같이 TEMPORARY keyword를 사용하면 됩니다.

ADD JAR 'hdfs:///user/suewoon';
CREATE TEMPORARY FUNCTION strip AS 'com.naverncp.hive.Strip'

빌드한 strip 함수가 제대로 수행되는지 확인합니다. 공백이 제거된 것을 확인 할 수 있습니다.

hive> select strip('  bee  ') from dummy;
converting to local hdfs:///user/suewoon/hive-1.0-SNAPSHOT.jar
Added [/tmp/70e2e17a-ecca-41ff-9fe6-48417b8ef797_resources/hive-1.0-SNAPSHOT.jar] to class path
Added resources: [hdfs:///user/suewoon/hive-1.0-SNAPSHOT.jar]
OK
bee
Time taken: 0.967 seconds, Fetched: 1 row(s)
hive> select strip('banana', 'ab') from dummy ;
OK
nan
Time taken: 0.173 seconds, Fetched: 1 row(s)

함수 삭제는 아래와 같이 수행하면 됩니다.

DROP FUNCTION strip

마치며

이번 페이지에서는 Hive UDF를 빌드하여 Cloud Hadoop에서 사용하는 방법을 알아봤습니다. 데이터의 특성에 따라, 자주 쓰게 되는 로직을 UDF로 만들어 두면 쉽게 SQL 구문으로 데이터를 조회 할 수 있습니다.

--

--