Link Search Menu Expand Document

개요

Cloudera Document 6.1 Hive UDF 문서를 참고하여 Cloudera CDH 플랫폼에서 HIVE UDF 를 작성하는 방법을 알아봅니다.
또한 MySQL의 Collate 와 문자열 유니코드에 대해서도 간단히 알아보겠습니다.

저의 이번 경우는 HIVE로 집계된 데이터를 SQOOP으로 export 시에 오류가 발생하였습니다.
기존에 설계된 MySQL 디비의 테이블 칼럼 Collate 속성이 utf8-general-ci 로 설계되어 있어 Key 칼럼에 Accent 문자열을 포함하는 문자열 데이터 자정시에 상황에 따라 키중복이 발생했던 것이었습니다.

칼럼의 Collate 속성이 utf8-general-ci 일경우 아래와 같은 경우 ã 문자열은 저장이 되겠지만 a 문자열 저장시 Key 중복 오류가 발생합니다.

insert into test(word) values ('ã');
insert into test(word) values ('a');

왜 그럴까요? Collate 는 문자열 정렬에 관련한 속성으로 값의 비교정책입니다. utf-general-ci 는 대소문자, 악센트 문자등을 같은 값으로 비교판단합니다. 그렇기 때문에 ã 문자는 a 취급되고 키중복이 발생합니다.

제 경우에 테이블의 변경할 수 없는 상황으로 HIVE 쿼리 수행시에 악센트 문자열을 제거 하는 작업을 수행했습니다.

UDF JAR

Maven 에 HIVE dependency를 추가합니다.

묶여진 Jar 파일은 모든 디펜던시를 포함하고 있어야 하는데요. Uber Jar 로의 빌드가 필요합니다. maven-shade-plugin 을 사용합니다.

<dependency>
    <groupid>org.apache.hive</groupid>
    <artifactId>hive-exec</artifactId>
    <version>2.1.1</version>
</dependency>

NaverD2 한글인코딩 에 잘 설명되어 있어 있어 많은 참고가 되었습니다. 우선 악센트를 포함하는 문자열을 정준분해(NFD) 한후 정규식을 통해 캐릭터를 치환한후 다시 정준결합(NFC) 하는 HIVE UDF 함수를 작성합니다. 이라는 문자열을 정준분해 분해 할경우 ㄱㅏㄱ 이 됩니다.

string = Normalizer.normalize(string, Normalizer.Form.NFD);
string = string.replaceAll("\\p{InCombiningDiacriticalMarks}+","");
string = Normalizer.normalize(string, Normalizer.Form.NFC);
package bigdata.hive.udf;


import lombok.extern.slf4j.Slf4j;
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.StringObjectInspector;

import java.text.Normalizer;

@Slf4j
@Description(
        name="str_normal",
        value="remove special character. ex) accent",
        extended="step1. Normalizer.Form.NFD" +
                "step2. replaceAll regular InCombiningDiacriticalMarks" +
                "step3. Normalizer.Form.NFC" )
public final class StringNormalize extends GenericUDF {

    StringObjectInspector stringOI;

    @Override
    public ObjectInspector initialize(ObjectInspector[] objectInspectors) throws UDFArgumentException {

        if (objectInspectors.length != 1) {
            throw new UDFArgumentLengthException("required string argument.");
        }

        ObjectInspector objectInspector = objectInspectors[0];

        if (false == objectInspector instanceof StringObjectInspector) {
            throw new UDFArgumentException(String.format( "argument must be a string. class=%s", objectInspector.getClass()));
        }

        this.stringOI = (StringObjectInspector) objectInspector;

        return PrimitiveObjectInspectorFactory.javaStringObjectInspector;

    }

    @Override
    public Object evaluate(DeferredObject[] deferredObjects) throws HiveException{

        DeferredObject deferredObject = deferredObjects[0];

        String string = this.stringOI.getPrimitiveJavaObject(deferredObject.get());

        if (string == null || string == "") {
            return string;
        }

        string = Normalizer.normalize(string, Normalizer.Form.NFD);
        string = string.replaceAll("\\p{InCombiningDiacriticalMarks}+","");
        string = Normalizer.normalize(string, Normalizer.Form.NFC);

        return string;

    }

    @Override
    public String getDisplayString(String[] strings) {
        return "String normalize. ";
    }
}

Hive UDF

Jar 파일을 HADOOP 파일시스템 hdfs:///app/hive-udf/hive-udf.jar 경로에 복사합니다. 또한 HIVE SERVER2 를 서비스 하고 있는 호스트의 로컬 파일 시스템에도 /usr/local/bigdata/hive-udf/hive-udf.jar 저장합니다.

빌드된 jar 파일은 HADOOP과 HIVE SERVER2의 서버에 모두 등록되어이 있어야 합니다.

HIVE 서비스가 Jar 를 classpath 에 등록하기 위해서는 아래의 설정값을 변경해 주어야 합니다. 설정값을 변경 한후 HIVE 구성파일을 재배포 하고 서비스를 재시작 합니다.

reloadable 속성을 정의 할경우 추후 Jar 파일이 변경될 경우 reload; 명령어를 사용할 수 있습니다.

Cloudera Manager > Hive > Configuration > Filter : Hive Serivce-Wide Advanced > Hive Service Advanced Configuration Snipped(Safety Valve) for hive-site.xml Click(+)

Name : hive.reloadable.aux.jars.path
Value : /usr/local/bigdata/hive-udf

beeline 혹은 hue 의 HIVE Editor 에서 함수를 등록 합니다.

create function str_normal  as 'bigdata.hive.udf.StrignNormalize' using jar 'hdfs:///app/hive-udf/hive-udf.jar';
describe function extended str_normal;
show functions;
select str_normal('ã'));
--- result
a

UDF 함수 업데이트

먼저 HIVE UDF 함수를 제거합니다.

drop function if exists str_normal; 

HADOOP 과 HIVESERVER2 의 Jar 파일을 업데이트 한 후 beeline 혹은 Hue 에서 reload; 명령어를 수행합니다.

HUE Editor 에서 SQL을 작성하실 때 HIVESERVER2 의 호스트 서버에 Jar 파일을 등록하지 않고 진행 않아도 UDF 를 사용할 수 있습니다. 하지만 이럴경우 Oozie Workflow 를 실행하거나 Schedule 로 등록시 UDF 함수를 찾을 수 없다는 오류가 발생합니다.