[HIVE] 파티션 타입 및 종류

😃

Hive 파티션(partition)의 개념은 RDBMS 와 크게 다르지 않다. 테이블을 하나 이상의 키로 파티셔닝(partitioning) 할 수 있다. 기본적으로 테이블 생성 시 DDL문을 통해 파티션키 유무를 정할 수 있지만, row가 많은 Fact 테이블 같은 경우는 선택이 아닌 필수이다. 데이터를 조회 할 때 파티션 키 값을 잘 구성해야 hive 내부적으로 skip-scan이 발생하여 불필요한 I/O를 최소화 할 수 있다. 또한 파티션 키의 순서에 따라 hdfs 상의 디렉토리 구조가 결정됨으로 워크로드에 따라 그 순서도 적절히 결정해야 한다. 그러면 간단히 Hive 파티션에 대해서 알아보자.


1. 파티션 테이블 생성

일반적으로 hive의 non-partition 테이블은 아래와 같이 생성할 수 있다.

create table salesdata_source(
    salesperson_id int,  
    product_id int,
    date_of_sale string
)

위의 테이블에서 date_of_sale 칼럼을 partition key로 설정하기 위해서는 아래와 같이 partitioned by 를 사용하면 된다.

create table salesdata (
    salesperson_id int,
    product_id int
)partitioned by (date_of_sale string)

위와 같이 partition 테이블을 생성하면 ‘desc salesdata’ 를 수행해도 date_of_sale 칼럼은 일반 칼럼으로 보여지는 것이 아닌 partition key로 보여지게 된다.


2. 파티셔닝 방법

파티션 테이블에 데이터를 넣는 방법은 다양하다. 일단 파티셔닝 방법에 따라서 아래와 같이 나눌 수 있다.

  • 정적 파티션 (static partition)
  • 동적 파티션 (dynamic partition)

다음으로 데이터 적재 방법에 따라 아래와 같이 나눌 수 있다.

  • non-partition테이블/text 테이블에서 columnar partition 테이블로 insert overwrite 수행
  • 특정 일자와 같이 항상 새로운 partition key의 값으로 데이터가 추가(append)되는 경우 직접 hdfs에 데이터 업로드 후 msck repair로 메타 정보를 업데이트 ( 외부 파티션 테이블, external partitioned table)

각각의 방법은 업무에 따라서 선택적으로 활용할 수 있다.

2.1 정적 파티션

정적 파티션은 data 삽입을 위해서 partition의 값을 명시적으로 입력해야 한다. 예를들어 아래와 같이 salesdata_source 에서 ‘10-27-2017’ , ‘10-28-2017’ 두 값으로 가져온 데이터를 파티셔닝 된 salesdata 에 넣으면서 파티션 값을 설정할 수 있다.

insert into table salesdata partition (date_of_sale='10-27-2017') \
select * from salesdata_source where date_of_sale='10-27-2017';

insert into table salesdata partition (date_of_sale='10-28-2017') \
select * from salesdata_source where date_of_sale='10-28-2017';

결과적으로 아래와 같은 결과를 확인할 수 있다.

show partitions salesdata;	
O/p:
date_of_sale='10-27-2017'
date_of_sale='10-28-2017'

전체 데이터를 조회하면 값이나 출력 결과가 달라진 것은 없다. 하지만 내부적으로 데이터 들은 생성된 두 개의 파티션에 데이터가 별도로 저장되어 있고, 그곳으로부터 데이터를 읽어온다. 이것은 HDFS (or Object Storage) 에서 아래와 같이 값을 확인할 수 있다.

hadoop fs -ls /apps/hive/warehouse/db_name.db/salesdata/
O/p:
/apps/hive/warehouse/db_name.db/salesdata/date_of_sale=10-27-2017
/apps/hive/warehouse/db_name.db/salesdata/date_of_sale=10-28-2017	

2.2 동적 파티션

다른 방식으로 insert 하는 데이터가 가지고 있는 파티션 칼럼의 값을 기반으로 동적으로 파티션닝 할 수도 있다. 이것을 사용하기 전에 동적 파티션을 위해 아래와 같이 parameter 설정이 필요하다.

set hive.exec.dynamic.partition=true;     
 --true: hive가 동적 파티션을 수행할 수 있도록 허용 

set hive.exec.dynamic.partition.mode=nonstrict;   
 -- strict: 최소 하나의 정적 파티션이 있어야 한다.(human error 방지) 
 -- nonstrict: 모든 파티션 키가 동적으로 생성된다. 

set hive.exec.max.dynamic.partitions=1000;    
 -- 동적으로 생성될 수 있는 파티션 최대 개수 (기본값:1000)
 
set hive.exec.max.dynamic.partitions.pernode=100; 
 -- mapper/reducer 노드별 생성될 수 있는 파티션 수 (기본값:100)
 
set hive.optimize.sort.dynamic.partition=true;
 -- 전역적으로 파티션 키에 대한 정렬 작업을 수행하여 하나의 파티션 키에 대한 reducer가 1개만 수행되도록 한다. 

set hive.merge.tezfiles = true 
 -- tez작업결과 일정 크기 이하의 파일들을 합쳐준다. (기본값:false)
 
set hive.merge.smallfiles.avgsize=32000000;
 -- 설정한 사이즈 이하의 파일을 합친다. (기본값:약16MB)

set hive.merge.size.per.task =256000000;
 -- merge된 파일의 크기 (기본값:약256MB)

동적 파티션의 경우 편리하게 파티셔닝을 할 수 있다는 장점과 함께 너무 작은 파일이 생길 수 있다는 단점이 있다. 작은 파일이 너무 많이 생기는 경우, 조회작업이 느려지는 현상이 발생할 수 있기 때문에 merge와 관련된 옵션을 같이 사용하는 것이 좋다.


3.파티션의 필요성

위에서 생성한 테이블에 다음과 같은 쿼리를 수행해보자.

select * from salesdata_source where salesperson_id=12 and date_of_sale ='10-28-2017'

salesdata_source 는 파티션이 되어 있지 않기 때문에 2017년 10월 28일 데이터를 읽기 위해서 full-scan 작업을 거쳐야 한다. 그러나 동일한 쿼리를 파티션이 된 salesdata 에 수행하는 경우, 10-28-2017 의 값을 갖는 파티션만 탐색하면 됨으로 훨씬 작은 자원과 시간으로 쿼리를 수행할 수 있다. 이러한 특성은 테이블이 커질 수록 점점 더 큰 성능 향상을 나타낸다.


4. 외부 파티션 테이블 (external partitioned table)

앞서 이야기한 것 처럼 파티션 구조에 맞도록 hdfs에 업로드 한 이후 hive 테이블의 파티션을 생성할 수도 있다. 만약 ‘/user/hive/text1.dat’ 안에는 다음과 같이 파일 내용이 있다고 해보자

salesperson_id|product_id|date_of_sale
12|101|10-27-2017
10|10010|10-27-2017	
111|2010|10-27-2017

위의 파일을 이용해서 아래와 같이 파티션 테이블을 생성할 수 있다.

create external table salesdata_ext
(salesperson_id int,
product_id int)
partitioned by (date_of_sale string)
location '/user/hive/salesdata_ext/'

그리고 아래와 같이 수동으로 partition 경로를 생성 한 후 데이터 파일을 해당 경로에 넣어 주면 된다.

hadoop fs -mkdir /user/hive/salesdata_ext/date_of_sale=10-27-2017
hadoop fs -cp /user/hive/text1.dat /user/hive/salesdata_ext/date_of_sale=10-27-2017/	

데이터는 넣어 주었지만 hive에는 아직 파티션에 대한 메타정보는 생성이 되지 않았기 때문에 partition 정보가 조회가 되지 않는다. 따라서 msck repair 를 통해서 정보를 갱신해 줘야 한다.

show partitions salesdata_ext;
 -- 파티션 조회가 되지 않음 
msck repair table salesdata_ext;   -- msck repair로 테이블 메타 조정 
show partitions salesdata_ext;
O/p: 
date_of_sale=10-27-2017

-- 또는 명시적으로 partition을 추가 함 
alter table salesdata_ext add partition (date_of_sale='10-27-2017');
show partitions salesdata_ext;
O/p:
date_of_sale=10-27-2017

5.파티션 수정

Drop 파티션, rename 파티션을 위해서는 alter 명령어를 통해 수행할 수 있다.

--파티션 이름 변경 
alter table salesdata partition (date_of_sale=10-27-2017) rename to partition (date_of_sale=10-27-2018);

-- 파티션 삭제 (내부 테이블)
alter table salesdata drop partition (date_of_sale=10-27-2017) ; 
o/p: (internal table)

-- 파티션 삭제 (외부 테이블)
alter table salesdata_ext drop partition (date_of_sale=10-27-2017) ; 
o/p: (external table) 

내부 테이블의 경우 파티션 삭제 시 warehouse 의 하위 경로의 데이터도 같이 삭제가 되지만, 외부 테이블의 경우 파티션 삭제 시 warehouse 의 하위 경로는 삭제되지 않는다.

반대로 warehouse 의 하위 파일들을 삭제하고 파티션을 삭제하지 않는다면, hive 관리 테이블에서 drop partition 하지 않는 이상 해당 파티션의 정보가 보인다.


Hive기반의 시스템을 운영하거나 개발하는 사람들을 만나보면 적지 않게, hive의 성능에 대해서 불만을 이야기 한다. 이야기를 나누다 보면 안타까움을 느끼게 되는데 첫번째는 hive에서 RDBMS의 성능을 기대한다는 점. 두번째는 별다른 최적화 없이 사용하고 있다는 점이다.

파티션을 만들고 관리하는 것은 hive의 시작이라고 할 수 있다. RDBMS에는 트랜잭션 단위의 데이터 처리에 대한 응답성은 매우 빠르지만, 페타 단위의 큰 데이터가 적재되지도 않거니와 테라 단위의 데이터를 쿼리한다는 것은 매우 어려운 일이다. 하지만 Hive 같은 경우 대용량의 데이터를 높은 처리량(throughput)으로 처리할 수 있도록 설계되었기 때문에 작은 데이터를 처리하거나 응답성이 좋은것을 기대하는 건 무리이다.

처리하는 데이터의 단위가 다르기 때문에 I/O에 더 민감하다. 그래서 RDBMS에서 인덱스설계를 고심하듯, Hive에서는 반드시 파티션에 대한 설계를 공들여야 한다. 그러고나면 Hive가 보장해 줄 수 있는 최소한의 성능을 제공받을 수 있을 것이다.