같은 Hive Metastore 를 보는 테이블인데 엔진에 따라 시각이 9시간 어긋난다. Trino 로 적재한 값을 Hive 와 Trino 에서 조회하면 맞는데 Impala 에서만 9시간 차이가 난다. 데이터가 잘못된 것이 아니라 엔진마다 TIMESTAMP 의 의미가 다르기 때문이다.
Parquet 의 TIMESTAMP 는 파일 안에 시간대 정보를 담는 방식과 담지 않는 방식이 모두 존재한다. 각 엔진은 자기 규칙으로 해석한다.
| 엔진 | 저장 | 조회 |
|---|---|---|
| Hive | 로컬 시각을 UTC 로 바꿔 기록하는 것이 현재 기본이다 | UTC 로 읽어 세션 시간대로 되돌린다 |
| Trino | timestamp 는 시간대가 없는 값으로 다루며, 파일에 기록된 값을 그대로 쓴다 |
카탈로그의 시간대 설정에 따라 보정한다 |
| Impala | TIMESTAMP 는 시간대가 없는 값이다. 기록한 값을 그대로 저장하고 그대로 보여 준다 | 변환 옵션이 켜져 있을 때만 UTC 로 간주해 로컬로 바꾼다 |
정리하면 Impala 만 "쓰인 그대로" 를 기본으로 하고, Hive 와 Trino 는 UTC 를 경유한다. 그래서 KST 환경에서 9시간이 어긋난다.
Impala 에는 Hive 가 쓴 Parquet 을 UTC 로 간주해 변환하는 옵션이 있다. CDP 에서는 이 값이 켜져 있는 경우가 많다.
SET convert_legacy_hive_parquet_utc_timestamps;
SET convert_legacy_hive_parquet_utc_timestamps=true;
SET timezone='Asia/Seoul';
이 옵션은 파일에 기록된 작성자(writer) 정보가 Hive 일 때만 적용된다. Trino 나 Spark 가 쓴 파일에는 해당하지 않으므로, 적재 엔진을 바꾸면 같은 테이블 안에서 파일마다 동작이 갈릴 수 있다.
Hive 쪽에는 Parquet 기록 시 옛 변환 방식을 쓸지 정하는 설정이 있다.
SET hive.parquet.timestamp.write.legacy.conversion.enabled=false;
true 면 JVM 의 로컬 시간대를 반영한 옛 방식으로 쓰고, false 면 UTC 기준으로 쓴다. 기본값은 배포판과 버전에 따라 다르므로 운영 클러스터에서 직접 조회해 확인한다(확인 필요).
Trino 는 카탈로그 속성으로 Parquet · ORC 의 시간대를 고정할 수 있다. 지정하지 않으면 JVM 기본 시간대를 따르므로 서버마다 결과가 달라질 여지가 있다.
hive.parquet.time-zone=UTC
hive.orc.time-zone=UTC
가장 안전한 방향은 저장은 UTC 로 일원화하고 표시할 때만 변환하는 것이다. 새로 만드는 파이프라인이라면 이 규칙을 정하고 엔진별 설정을 거기에 맞춘다.
이미 쌓인 데이터를 건드릴 수 없다면 조회 쪽에서 보정한다.
SELECT from_utc_timestamp(ts_col, 'Asia/Seoul') AS ts_kst FROM db.tbl;
시간대 문제를 아예 피하려면 컬럼 타입을 바꾸는 방법도 있다. Iceberg 테이블은 timestamptz 를 지원하므로 시간대 정보를 값에 담을 수 있고, 문자열로 YYYY-MM-DD HH:MM:SS 를 저장하면 변환이 일어나지 않는다. 다만 정렬과 연산 비용이 달라지므로 신규 설계에서만 고려한다.
적재 직후 같은 행을 세 엔진에서 조회해 비교한다. 파일 수준에서 무엇이 기록됐는지 보려면 Parquet 메타데이터를 직접 읽는다.
parquet-tools meta hdfs:///warehouse/db.db/tbl/part-00000.parquet | head -30
작성자(created_by) 문자열과 컬럼의 논리 타입(isAdjustedToUTC 여부)을 확인하면 어느 엔진 규칙이 적용될지 판단할 수 있다.