Hive on Tez 쿼리가 Vertex failed, vertexName=Map 1, ... diagnostics: Vertex did not succeed due to OWN_TASK_FAILURE 로 끝난다. 메시지만으로는 원인을 알 수 없고, 실패한 태스크의 로그를 봐야 한다.
OWN_TASK_FAILURE 는 그 vertex 자신의 태스크가 실패했다는 뜻이고, OTHER_VERTEX_FAILURE 는 앞단 vertex 가 실패해 연쇄로 죽었다는 뜻이다. 후자는 원인 vertex 를 먼저 찾아야 한다.
컨테이너 로그를 받아 실제 예외를 본다. YARN 애플리케이션 ID 는 Hive 콘솔 출력이나 ResourceManager UI 에서 얻는다.
yarn application -list -appStates RUNNING
yarn logs -applicationId application_1730000000000_12345 > app.log
grep -nE "OutOfMemoryError|beyond physical memory|Killed by|Caused by" app.log | head -50
로그에서 갈리는 지점은 세 가지다. Container is running beyond physical memory limits 는 컨테이너 메모리 부족, java.lang.OutOfMemoryError: Java heap space 는 JVM 힙 부족, 특정 리듀서 하나만 오래 돌다 죽으면 데이터 skew 다.
OOM 이 HiveServer2 프로세스 자체에서 났다면 쿼리 설정이 아니라 HS2 의 힙(HiveServer2 Java Heap Size)을 봐야 한다. 결과를 클라이언트로 대량 fetch 하거나 큰 파티션 목록을 다룰 때 HS2 힙이 먼저 터진다.
컨테이너 크기와 JVM 힙을 함께 올린다. 힙은 컨테이너의 70~80% 로 두어야 하며, 힙만 올리면 YARN 이 컨테이너를 죽인다.
SET hive.tez.container.size=8192;
SET hive.tez.java.opts=-Xmx6144m;
SET tez.am.resource.memory.mb=4096;
컨테이너 크기는 YARN 의 yarn.scheduler.minimum-allocation-mb 배수로 올림되고 yarn.scheduler.maximum-allocation-mb 를 넘을 수 없다. 이 한계를 넘겨 잡으면 컨테이너 할당 자체가 실패한다.
MapReduce 엔진이라면 대응 설정이 다르다.
SET mapreduce.map.memory.mb=4096;
SET mapreduce.map.java.opts=-Xmx3072m;
SET mapreduce.reduce.memory.mb=4096;
SET mapreduce.reduce.java.opts=-Xmx3072m;
Hive 3 이후로는 MapReduce 실행 엔진이 지원 대상에서 빠졌다. SET hive.execution.engine=mr 로 우회하는 방법은 CDP 를 포함한 현행 배포판에서 쓸 수 없다.
조인 키가 한쪽으로 몰리면 리듀서 하나가 전체 데이터를 받아 OOM 이 난다. 리듀서 수를 늘리고 skew join 최적화를 켠다.
SET hive.exec.reducers.bytes.per.reducer=268435456;
SET hive.optimize.skewjoin=true;
SET hive.tez.auto.reducer.parallelism=true;
근본 대응은 키 분포를 고치는 것이다. NULL 이나 기본값이 몰린 키는 조인 전에 걸러내거나 랜덤 접미사를 붙여 흩는다.
입력 스플릿이 잘게 쪼개지면 태스크가 수천 개로 늘어 AM 이 먼저 지친다. 그룹핑 크기를 키워 태스크 수를 줄인다.
SET tez.grouping.min-size=134217728;
SET tez.grouping.max-size=1073741824;
작은 파일이 많은 테이블이 원인이면 설정보다 파일 병합이 먼저다. 파티션을 다시 쓰거나 INSERT OVERWRITE 로 재작성해 파일 크기를 블록 크기 수준으로 맞춘다.