Kudu 의 UNIXTIME_MICROS 타입(Impala 에서는 TIMESTAMP)은 에포크로부터의 마이크로초 하나만 저장한다. 타임존 정보를 담지 않으며, 저장된 값은 항상 UTC 기준으로 해석된다. 화면에 보이는 시각이 달라지는 것은 조회하는 엔진의 타임존 설정 때문이지 저장 값이 다른 것이 아니다.
따라서 적재 경로에서 지켜야 할 것은 하나다. 원본 시각이 어느 타임존 기준인지 확정하고, UTC 로 변환한 뒤 넣는다.
MSSQL 의 datetime · datetime2 는 타임존 정보를 갖지 않는다. 값이 KST 로 기록된 것인지 UTC 로 기록된 것인지는 데이터로 알 수 없으므로 업무 규칙으로 확정해야 한다. 확정하지 못한 채 적재하면 9시간이 어긋난 데이터가 그대로 쌓인다.
rowversion(예전 이름 timestamp)은 시각이 아니라 변경 순서를 나타내는 바이너리 값이다. 증분 추출 키로는 쓸 수 있지만 시각 컬럼으로 변환할 수 없다.
QueryDatabaseTableRecord 나 ExecuteSQLRecord 로 읽은 뒤 PutKudu 로 넣는 구성에서는 스키마의 논리 타입을 맞추는 것이 핵심이다.
| SQL 타입 | Avro 표현 |
|---|---|
DATE |
int + logicalType: date |
TIMESTAMP |
long + logicalType: timestamp-millis 또는 timestamp-micros |
TIME |
int + logicalType: time-millis |
{
"type": "record",
"name": "row",
"fields": [
{ "name": "id", "type": "long" },
{ "name": "updated_at",
"type": { "type": "long", "logicalType": "timestamp-micros" } }
]
}
문자열로 들어오는 시각은 UpdateRecord 에서 변환한다.
/updated_at = toDate(/updated_at, "yyyy-MM-dd HH:mm:ss.SSS")
NiFi 의 날짜 파싱 함수는 타임존을 지정하지 않으면 JVM 기본 타임존으로 해석한다. NiFi 노드의 타임존이 서버마다 다르면 같은 플로우가 노드에 따라 다른 값을 넣게 되므로, NiFi 프로세스의 user.timezone 을 통일하거나 포맷 문자열에 타임존을 명시한다.
SET TIMEZONE='Asia/Seoul';
SELECT updated_at,
CAST(updated_at AS STRING) AS as_stored,
FROM_UTC_TIMESTAMP(updated_at, 'Asia/Seoul') AS kst
FROM db.tbl
LIMIT 10;
TO_UTC_TIMESTAMP · FROM_UTC_TIMESTAMP 로 명시 변환하면 세션 타임존에 좌우되지 않는다. Impala 세션 타임존이 다르면 같은 행이 다른 시각으로 보이므로, 검증할 때는 항상 변환 함수를 붙여 비교한다.
DATE 타입도 있다(Kudu 1.12 이상). 시각이 필요 없는 컬럼은 DATE 로 두는 편이 저장과 비교 모두 단순하다.