특정 쿼리 하나에서만 java.lang.OutOfMemoryError: Java heap space 로 프로세스가 죽는다. 데이터를 읽지 않는 EXPLAIN 만 실행해도 같은 증상이 나온다.
EXPLAIN 은 실행 계획만 만든다. 이 단계에서 힙이 터진다면 문제가 데이터 처리량이 아니라 컴파일 단계의 메모리 사용에 있다는 뜻이다. 죽는 주체가 어디인지 먼저 확인한다.
| 주체 | 확인 방법 |
|---|---|
| Beeline/Hive CLI 클라이언트 JVM | 클라이언트 콘솔에 OOM 이 찍히고 서버는 멀쩡하다 |
| HiveServer2 | HS2 로그에 OOM. 다른 세션도 함께 끊긴다 |
| Tez AM 또는 컨테이너 | YARN 애플리케이션 로그에 OOM |
파티션 수가 많은 테이블을 참조하면 컴파일 과정에서 파티션 메타데이터를 모두 가져온다. 수만 개 파티션을 훑는 쿼리는 계획 수립만으로도 큰 메모리를 쓴다. 조인 대상이 많거나 뷰가 중첩돼 계획 트리가 커지는 경우, IN 목록이 수천 건인 경우도 같다.
가장 먼저 할 일은 쿼리가 실제로 건드리는 파티션 수를 줄이는 것이다. 파티션 조건을 상수로 명시하면 컴파일 단계에서 가지치기가 된다. 함수로 감싼 조건(WHERE to_date(dt) = ...)은 가지치기를 막는다.
SELECT ... FROM t WHERE dt = '2024-11-27';
클라이언트 JVM 이면 환경 변수로 준다.
export HADOOP_CLIENT_OPTS="-Xmx4g $HADOOP_CLIENT_OPTS"
beeline -u "jdbc:hive2://hiveserver01:10000/default"
HiveServer2 라면 서비스의 힙 설정을 올린다. Cloudera Manager 나 Ambari 에서는 Java Heap Size of HiveServer2 항목이다. 직접 관리한다면 hive-env.sh 의 HIVE_SERVER2_OPTS 에 둔다. 서비스 재시작이 필요하다.
Tez 쪽이 문제라면 AM 과 컨테이너 크기를 조정한다.
SET hive.tez.container.size=4096;
SET tez.am.resource.memory.mb=4096;
SET tez.runtime.io.sort.mb=1024;
hive.tez.container.size 를 올릴 때는 hive.tez.java.opts 의 -Xmx 도 함께 맞춘다. 컨테이너 크기만 키우고 JVM 힙을 그대로 두면 효과가 없다.
서브쿼리를 임시 테이블로 나눠 단계별로 처리하면 계획 하나의 크기가 줄어든다. 불필요한 조인과 컬럼을 걷어내고, 큰 IN 목록은 임시 테이블 조인으로 바꾼다. CTE 를 여러 번 참조하면 그만큼 계획이 커지므로 중간 결과를 실제 테이블로 떨어뜨린다.