Spark + Iceberg - 2(Partition, Schema evolution)
소개
아이스버그 기능 Partition,Schema Evolution에 대해서 살펴보자.
기존 하이브 테이블은 파티셔닝을 변경할 수 없다.
그래서 일별 단위에서 시간별 단위로 변경하려면 새로운 테이블을 만들어야한다.
쿼리도 재작성해야하므로 불편함이 존재한다.
이것을 해결하기 위해서 아이스버그는 Partitioning Evolution을 제공한다.
그리고 Schema에 대한 Evolution을 관리할 수 있다.
아래 예시는 Schema Evolution이 적용되는 예시이다.
1. 테이블에 새로운 컬럼을 추가하는 경우
2. 테이블에 존재하는 컬럼을 삭제하는 경우
3. 컬럼명을 새로 변경했을 경우
4. 컬럼 타입을 변경했을 경우
5. 테이블 컬럼 순서를 변경했을 경우
위 변경들은 메타 데이터 변경이므로 데이터 파일에는 영향이 가지 않는다.
Schema Evolution Example
1. CREATE TABLE 후 확인
CREATE TABLE schemaev (
ID BIGINT,
NAME STRING
)
USING iceberg
LOCATION 's3a://martinispark/schemaev'
2. 새로운 COLUMN 추가
ALTER TABLE schemaev ADD COLUMNS (
new_column string comment 'new'
);
JSON 파일이 하나 추가된 것을 볼 수 있다.
3. INSERT INTO Data
INSERT INTO schemaev VALUES (1,'martini','new1'), (2,'jack','new2')
JSON 파일 내부이다.
변경 이력까지 JSON으로 저장되어서 데이터가 저장될 때 마다 가지고 다닌다.
....
"schemas" : [ {
"type" : "struct",
"schema-id" : 0,
"fields" : [ {
"id" : 1,
"name" : "ID",
"required" : false,
"type" : "string"
}, {
"id" : 2,
"name" : "NAME",
"required" : false,
"type" : "string"
} ]
}, {
"type" : "struct",
"schema-id" : 1,
"fields" : [ {
"id" : 1,
"name" : "ID",
"required" : false,
"type" : "string"
}, {
"id" : 2,
"name" : "NAME",
"required" : false,
"type" : "string"
}, {
"id" : 3,
"name" : "new_column",
"required" : false,
"type" : "string",
"doc" : "new"
} ]
} ],
...
"manifest-list" : "s3a://martinispark/schemaev/metadata/snap-9156582206091879578-1-15c2ced9-b970-4aa3-b204-27dc37b231a3.avro",
AVRO 파일은 웹 뷰어로 보니 실제 데이터에 대한 정보를 담고있다.
[
{
"status":1,
"snapshot_id":9156582206091879578,
"sequence_number":null,
"file_sequence_number":null,
"data_file":{
"content":0,
"file_path":"s3a://martinispark/schemaev/data/00000-6-2514eb04-1b8a-4a07-82d7-7fd7427e75f4-00001.parquet",
"file_format":"PARQUET",
"partition":{
},
"record_count":1,
"file_size_in_bytes":889,
"column_sizes":[
],
"value_counts":[
],
"null_value_counts":[
],
"nan_value_counts":[
],
"lower_bounds":[
],
"upper_bounds":[
],
"key_metadata":null,
"split_offsets":[
4
],
"equality_ids":null,
"sort_order_id":0
}
},
{
"status":1,
"snapshot_id":9156582206091879578,
"sequence_number":null,
"file_sequence_number":null,
"data_file":{
"content":0,
"file_path":"s3a://martinispark/schemaev/data/00001-7-2514eb04-1b8a-4a07-82d7-7fd7427e75f4-00001.parquet",
"file_format":"PARQUET",
"partition":{
},
"record_count":1,
"file_size_in_bytes":869,
"column_sizes":[
],
"value_counts":[
],
"null_value_counts":[
],
"nan_value_counts":[
],
"lower_bounds":[
],
"upper_bounds":[
],
"key_metadata":null,
"split_offsets":[
4
],
"equality_ids":null,
"sort_order_id":0
}
}
]
결과적으로 JSON은 테이블 메타 정보 파일, ARVO는 데이터 메타 정보 파일 느낌이다.
그리고 만약 새로운 컬럼이 추가되면 이전에 있던 데이터들은 NULL로 표시된다.
Partition Evolution Example
아래의 예시는 2008년은 월별, 2009년은 일별로 파티션이 나누어져 있다.
그림처럼 기존 테이블에 파티션을 업데이트 할 수 있다.
그럼 파티셔닝을 변경하는 실습을 진행해보자.
1. CREATE TABLE
CREATE TABLE partitionev (
ID STRING,
COUNTRY STRING,
CITY STRING
)
USING iceberg
LOCATION 's3a://martinispark/partitionev'
PARTITIONED BY (COUNTRY)
2. INSERT INTO Data
Country Column을 기준으로 저장하고 있다.
INSERT INTO partitionev VALUES
(1,'south-korea','seoul'),
(2,'south-korea','busan'),
(3,'japan','tokyo'),
(4,'japan','osaka'),
(5,'china','beijing'),
(6,'china','sanghai')
3. REPLACE PARTITION
기존 COUNTRY 파티션을 CITY로 변경해보자.
ALTER TABLE partitionev REPLACE PARTITION FIELD COUNTRY WITH CITY
4. INSERT INTO Data
INSERT INTO partitionev VALUES
(7,'usa','california'),
(8,'usa','new-york'),
(9,'canada','ottawa'),
(10,'canada','toronto')
5. 조회
위에서 보듯이 다르게 파티션을 저장하는 것을 알 수 있다.