진화

진화 (Evolution)

아이스버그는 제자리 테이블 진화(in-place table evolution)를 지원해요. SQL처럼 테이블 스키마를 진화시킬 수 있고, 중첩 구조에서도 가능하며, 데이터 볼륨이 변할 때 파티션 레이아웃도 바꿀 수 있어요. 이 문서에서는 스키마 진화, 파티션 진화, 정렬 순서 진화의 세 가지를 살펴볼게요. 핵심은 이 모든 진화가 메타데이터만 바꾸는 연산이라 데이터 파일을 다시 쓸 필요가 없다는 점이에요.

출처: 문서

본문

아이스버그는 제자리 테이블 진화를 지원해요. 중첩 구조에서도 SQL처럼 테이블 스키마를 진화시킬 수 있고, 데이터 볼륨이 변할 때 파티션 레이아웃도 바꿀 수 있어요. 아이스버그는 테이블 데이터를 다시 쓰거나 새 테이블로 마이그레이션하는 값비싼 작업을 요구하지 않아요.

예를 들어 하이브 테이블 파티셔닝은 변경할 수 없어서, 일 단위 파티션 레이아웃에서 시 단위 파티션 레이아웃으로 옮기려면 새 테이블이 필요해요. 그리고 쿼리가 파티션에 의존하기 때문에 새 테이블에 맞게 쿼리를 다시 작성해야 해요. 어떤 경우에는 컬럼 이름을 바꾸는 것처럼 간단한 변경조차 지원되지 않거나, 데이터 정확성 문제를 일으킬 수 있어요.

스키마 진화 (Schema evolution)

아이스버그는 다음 스키마 진화 변경을 지원해요.

  • 추가(Add) — 테이블이나 중첩 struct에 새 컬럼 추가
  • 삭제(Drop) — 테이블이나 중첩 struct에서 기존 컬럼 제거
  • 이름 변경(Rename) — 중첩 struct의 기존 컬럼이나 필드 이름 변경
  • 갱신(Update) — 컬럼, struct 필드, 맵 키, 맵 값, 리스트 요소의 타입 확장(widen)
  • 순서 변경(Reorder) — 중첩 struct에서 컬럼이나 필드의 순서 변경

아이스버그 스키마 업데이트는 메타데이터 변경이므로, 업데이트를 수행하기 위해 데이터 파일을 다시 쓸 필요가 없어요.

맵 키(map keys)는 평등(equality)을 바꾸는 struct 필드의 추가나 삭제를 지원하지 않는다는 점에 유의해주세요.

정확성 (Correctness)

아이스버그는 파일을 다시 쓰지 않으면서도 스키마 진화 변경이 서로 독립적이고 부작용이 없음을 보장해요.

  1. 추가된 컬럼은 다른 컬럼의 기존 값을 절대 읽지 않아요.
  2. 컬럼이나 필드를 삭제해도 다른 컬럼의 값은 변하지 않아요.
  3. 컬럼이나 필드를 갱신해도 다른 컬럼의 값은 변하지 않아요.
  4. struct에서 컬럼이나 필드의 순서를 바꿔도 컬럼·필드 이름과 연결된 값은 변하지 않아요.

아이스버그는 고유 ID를 사용해서 테이블의 각 컬럼을 추적해요. 컬럼을 추가하면 새 ID가 할당되므로 기존 데이터가 실수로 사용되지 않아요.

  • 컬럼을 이름으로 추적하는 포맷은 이름이 재사용되면 컬럼을 실수로 삭제 해제(un-delete)할 수 있는데, 이는 #1을 위반해요.
  • 컬럼을 위치(position)로 추적하는 포맷은 각 컬럼에 사용되는 이름을 바꾸지 않고는 컬럼을 삭제할 수 없는데, 이는 #2를 위반해요.

파티션 진화 (Partition evolution)

아이스버그 테이블 파티셔닝은 쿼리가 파티션 값을 직접 참조하지 않기 때문에 기존 테이블에서 업데이트할 수 있어요.

파티션 스펙을 진화시키면 이전 스펙으로 쓰여진 기존 데이터는 그대로 유지돼요. 새 데이터는 새 스펙으로 새 레이아웃에 쓰여져요. 각 파티션 버전의 메타데이터는 따로 보관돼요. 그래서 쿼리를 쓰기 시작하면 스플릿 계획(split planning)을 얻게 돼요. 각 파티션 레이아웃이 해당 파티션 레이아웃에 대해 파생된 필터를 사용해 파일을 각각 계획하는 방식이에요. 다음은 인위적으로 만든 예시의 시각적 표현이에요.

2008년 데이터는 월 단위로 파티셔닝돼요. 2009년부터 테이블이 업데이트되어 데이터가 대신 일 단위로 파티셔닝돼요. 두 파티셔닝 레이아웃이 같은 테이블에 공존할 수 있어요.

아이스버그는 숨겨진 파티셔닝을 사용하므로, 빠른 속도를 위해 특정 파티션 레이아웃에 맞게 쿼리를 작성할 필요가 없어요. 대신 필요한 데이터를 선택하는 쿼리를 작성하면, 아이스버그가 일치하는 데이터가 없는 파일을 자동으로 프루닝(prune)해요.

파티션 진화는 메타데이터 연산이며 파일을 미리 다시 쓰지 않아요.

아이스버그의 자바 테이블 API는 파티션 스펙을 업데이트하는 updateSpec API를 제공해요. 예를 들어 다음 코드로 id 컬럼 값을 8개 버킷에 넣는 새 파티션 필드를 추가하고 기존 파티션 필드 category를 제거할 수 있어요.

Table sampleTable = ...;
sampleTable.updateSpec()
    .addField(bucket("id", 8))
    .removeField("category")
    .commit();

스파크는 ALTER TABLE SQL 문을 통해 파티션 스펙 업데이트를 지원해요. 자세한 내용은 Spark SQL 문서를 확인해주세요.

정렬 순서 진화 (Sort order evolution)

파티션 스펙과 비슷하게, 아이스버그 정렬 순서도 기존 테이블에서 업데이트할 수 있어요. 정렬 순서를 진화시키면 이전 순서로 쓰여진 기존 데이터는 그대로 유지돼요. 엔진은 항상 최신 정렬 순서로 데이터를 쓰거나, 정렬이 너무 비싸면 정렬하지 않고(un-sorted) 쓰도록 선택할 수 있어요.

아이스버그의 자바 테이블 API는 정렬 순서를 업데이트하는 replaceSortOrder API를 제공해요. 예를 들어 다음 코드로 id 컬럼은 오름차순·null 마지막, category 컬럼은 내림차순·null 먼저 정렬하는 새 정렬 순서를 만들 수 있어요.

Table sampleTable = ...;
sampleTable.replaceSortOrder()
   .asc("id", NullOrder.NULLS_LAST)
   .dec("category", NullOrder.NULL_FIRST)
   .commit();

스파크는 ALTER TABLE SQL 문을 통해 정렬 순서 업데이트를 지원해요. 자세한 내용은 Spark SQL 문서를 확인해주세요.

더 알아보기 (Learn more)