하나의 Impala 테이블은 여러개의 Parquet 파일로 구성될 수 있는데, 이때 각 파케이 파일들의 컬럼 순서가 다를 수가 있다. 임팔라만을 이용해서 파케이 파일을 생성했다 하더라도 기존에 없던 컬럼이 이후에 rename 되는 경우 또는 다른 방식으로 생성된 파케이 파일을 impala 에서 추가로 로드하는 경우에 이런 상황이 발생할 수 있을 것 같다.
어쨋든 다음과 같은 에러가 발생하는 경우
File ‘hdfs://quickstart.cloudera:8020/user/hive/warehouse/schema_evolution_test/9246f35fb0c26132-8930434600000000_1994605024_data.0.parq’ has an incompatible Parquet schema for column ‘default.schema_evolution_test.c1’. Column type: STRING, Parquet schema: optional int32 c2 [i:0 d:1 r:0]
아래 세팅을 변경해주는 것으로 해결할 수 있다.
set PARQUET_FALLBACK_SCHEMA_RESOLUTION=name;
위 설정은 다음과 같이 두 개의 값을 가질 수 있는데, 기본값은 position
이다.
- PARQUET_FALLBACK_SCHEMA_RESOLUTION
- position
- name
CDH Demo Container 환경
테스트 환경은 아래 링크의 CDH 5.x 데모 버전 docker 이미지를 이용했으며
Download CDH-5.13.0 demo docker image
컨테이너 생성은, docker import 명령을 이용해서 이미지를 불러온 뒤
curl -O https://downloads.cloudera.com/demo_vm/docker/cloudera-quickstart-vm-5.13.0-0-beta-docker.tar.gz
tar -xvzf cloudera-quickstart-vm-*-docker.tar.gz
docker import - cloudera/quickstart:5.13 < cloudera-quickstart-vm-*-docker/*.tar
다음과 같은 명령으로 생성했다. Impala 테스트를 위해서는 Hue
환경만 있으면 충분하기 때문에 8888 포트만 매핑한다.
docker run \
--name cloudera.cluster.01 \
--hostname=quickstart.cloudera \
--privileged=true -t -i -d \
-p 8888:8888 \
cloudera/quickstart:5.13 /usr/bin/docker-quickstart
Windows 환경인 경우
Mac/Linux 가 아닌 Windows 환경의 경우 이미지를 불러올 때 type
을 이용해서 표준 입력으로 넣어주면 된다.
type cloudera-quickstart-vm-5.13.0-0-beta-docker.tar | docker import - cloudera/quickstart:5.13
PARQUET_FALLBACK_SCHEMA_RESOLUTION 관련 테스트
incompatible parquet schema 에러 재현 및 PARQUET_FALLBACK_SCHEMA_RESOLUTION 변경 테스트 시나리오
테스트 환경
Hue
는 웹브라우저에서 http://localhost:8888
경로에 접근한 뒤, id/pass
: cloudera/cloudera
로 로그인하면 된다.
Impala 재현 스크립트
Hue
에디터가 정상적으로 떴으면 아래 스크립트 순서대로 수행
-- 테스트를 위한 테이블 생성
create table default.schema_evolution_test (
c1 string
, c2 int
) stored as parquet;
-- 컬럼 순서가 테스트 테이블과 반대인 Parquet 파일 생성을 위한 임시 테이블 생성
create table default.schema_evolution_test_tmp (
c2 int
, c1 string
) stored as parquet;
-- 테스트 데이터 생성
insert into default.schema_evolution_test (
c1, c2
) values ('1',1);
insert into default.schema_evolution_test_tmp (
c1, c2
) values ('2',2);
-- 임시 테이블의 Parquet 파일 경로 조회
describe formatted default.schema_evolution_test_tmp;
--Location: hdfs://quickstart.cloudera:8020/user/hive/warehouse/schema_evolution_test_tmp
-- 컬럼 순서가 다른 두 Parquet 파일을 하나의 테이블에 로드
load data
inpath 'hdfs://quickstart.cloudera:8020/user/hive/warehouse/schema_evolution_test_tmp'
into table
default.schema_evolution_test;
-- PARQUET_FALLBACK_SCHEMA_RESOLUTION=position 인 상태에서 조회결과 에러 발생
select * from default.schema_evolution_test;
--File 'hdfs://quickstart.cloudera:8020/user/hive/warehouse/schema_evolution_test/9246f35fb0c26132-8930434600000000_1994605024_data.0.parq' has an incompatible Parquet schema for column 'default.schema_evolution_test.c1'. Column type: STRING, Parquet schema: optional int32 c2 [i:0 d:1 r:0]
-- 테스트 세션에 대해 PARQUET_FALLBACK_SCHEMA_RESOLUTION=name 변경
set PARQUET_FALLBACK_SCHEMA_RESOLUTION=name;
-- 정상 조회
select * from default.schema_evolution_test;
Spark, read parquet 테스트
위 테스트에서 생성된 두 Parquet 파일을 로컬에 내려받아, 로컬 spark-shell
에서 테스트한 결과, Spark 에서는 컬럼 오더가 다른 파일들을 읽더라도 key 값에 맞추어 스키마 매핑을 하는 것을 확인
scala> val sqlContext = new org.apache.spark.sql.SQLContext(sc)
scala> val myParq = sqlContext.read.parquet("*.parq")
scala> myParq.show()
+----+---+
| c1| c2|
+----+---+
|[31]| 1|
..
.
scala> myParq.printSchema()
root
|-- c1: binary (nullable = true)
|-- c2: integer (nullable = true)
scala> myParq.registerTempTable("tab")
scala> result.show()
+----+---+
| c1| c2|
+----+---+
|[31]| 1|
..
.
참고 링크
Parquet Schema Evolution
Cloudera 마스터의 코멘트
-
PARQUET_FALLBACK_SCHEMA_RESOLUTION
값은 position 보다는 name 을 기본으로 설정하는 것이 보다 직관적이며, 성능 차이는 크게 없음. -
position, name 중 어느 것을 고르는지에 따라 tradeoff 가 존재
position
으로 하는 경우, 컬럼 추가를 맨 뒤에 하는 것만 가능하지만 컬럼명 renaming 에 자유로움name
으로 하는 경우 컬럼 위치에 상관없이 add/drop 이 가능