Hive 0.14 부터 INSERT · UPDATE · DELETE 를 트랜잭션으로 처리하는 테이블을 지원한다. 이후 Hive 3 에서 구조가 크게 정리돼, 관리형(managed) 테이블은 기본적으로 ACID 트랜잭션 테이블로 만들어진다. 외부(external) 테이블은 여전히 비트랜잭션이다.
내부적으로는 원본을 고치지 않는다. 변경분을 델타 디렉터리에 쌓고, 읽을 때 베이스와 델타를 합쳐 보여 주며, compaction 이 주기적으로 델타를 베이스로 병합한다. 파일시스템이 임의 수정과 잠금을 제공하지 않으므로 택한 방식이다.
CREATE TABLE customer_acid (
id BIGINT,
name STRING,
city STRING
)
STORED AS ORC
TBLPROPERTIES ('transactional' = 'true');
UPDATE customer_acid SET city = 'Seoul' WHERE id = 1001;
DELETE FROM customer_acid WHERE id = 1002;
MERGE INTO customer_acid AS t
USING staging_customer AS s
ON t.id = s.id
WHEN MATCHED THEN UPDATE SET city = s.city
WHEN NOT MATCHED THEN INSERT VALUES (s.id, s.name, s.city);
전체 ACID 테이블은 ORC 포맷이어야 하고 버킷 구성이 필요했다. Hive 3 부터 버킷 없이도 만들 수 있고, 'transactional_properties'='insert_only' 를 주면 ORC 가 아닌 포맷에서도 삽입 전용 트랜잭션 테이블을 쓸 수 있다. 이 경우 UPDATE 와 DELETE 는 불가능하다.
서버 쪽에는 트랜잭션 관리자와 compaction 관련 설정이 켜져 있어야 한다.
SET hive.support.concurrency=true;
SET hive.txn.manager=org.apache.hadoop.hive.ql.lockmgr.DbTxnManager;
SET hive.compactor.initiator.on=true;
SET hive.compactor.worker.threads=2;
Metastore 백엔드 DB 에 트랜잭션 관련 테이블이 만들어져 있어야 하므로 schematool 로 스키마를 갱신한 상태여야 한다.
델타가 쌓이면 읽기 성능이 떨어지고 파일 수가 늘어난다. compaction 이 돌지 않으면 시간이 지날수록 나빠진다.
SHOW COMPACTIONS;
ALTER TABLE customer_acid COMPACT 'minor';
ALTER TABLE customer_acid COMPACT 'major';
minor 는 델타끼리 합치고, major 는 베이스와 델타를 모두 합쳐 새 베이스를 만든다. 자동 compaction 은 Metastore 의 Initiator 와 Worker 가 담당하므로 해당 스레드 설정이 0 이면 아무 일도 일어나지 않는다.
변경 이력을 반영해야 하는 마스터성 데이터, 개인정보 삭제 요구를 처리해야 하는 테이블, 소량 수정이 반복되는 테이블에 맞는다. 반대로 적재 후 변하지 않는 대용량 로그성 데이터에는 불리하다. 델타 관리와 compaction 비용만 늘고 얻는 것이 없다.
동일한 요구를 Iceberg 로 처리하는 선택지도 있다. 포맷 제약이 적고 여러 엔진에서 함께 쓰기 좋다. 새로 설계한다면 Hive ACID 와 Iceberg 를 함께 비교한다.
트랜잭션 테이블은 파일을 직접 건드리면 깨진다. 외부 도구로 디렉터리에 파일을 넣거나 지우는 방식으로 적재하면 안 된다. 반드시 SQL 로 넣는다.