티스토리 뷰
지난 실습에서 .parquet 라는 새로운 파일 포맷을 마주쳤다.
빅데이터를 효율적으로 저장하는 파일 포맷이라고 하는데
공식문서를 읽어보고 핵심적인 부분만 정리해보았다.
개요
Parquet는 효율적인 데이터 저장 및 검색을 위한 오픈소스로, 열(칼럼) 기반으로 데이터를 저장하는 파일 형식이다.
Parquet를 사용하면 크고 복잡한 데이터를 효율적(공간 효율, 속도)으로 읽고 쓸 수 있다.
1. 칼럼 기반 데이터 저장
아래와 같은 테이블이 있다고 가정해보자.
ID | Name | Age |
1 | John | 30 |
2 | Kim | 24 |
3 | Park | 24 |
4 | Lee | 15 |
5 | Alice | 28 |
기존 로우 기반의 데이터 저장 방식(csv)은 아래와 같이 저장한다.
ID,Name,Age
1,John,30
2,Kim,24
3,Park,24
4,Lee,15
5,Alice,28
6,Bob,28
반면 칼럼 기반의 데이터 저장 방식(parquet)는 아래와 같이 저장할 것이다.
ID,Name,Age
1,2,3,4,5,6
John,Kim,Park,Lee,Alice,Bob
30,24,24,15,28,28
그렇다면 칼럼 기반의 데이터 저장 방식이 어떤 이점을 갖느냐?
1. I/O 최적화
필요한 칼럼만 읽고 쓸 수 있기 때문에 불필요한 I/O를 줄일 수 있다.
2. 집계 연산 등 특정 작업에 유리
SUM, AVG, COUNT와 같이 전체 데이터를 집계하는 연산을 하는 경우 특정 칼럼만 집계할 수 있어서 성능이 좋다.
3. 칼럼마다 개별적인 압축과 인코딩
같은 칼럼 안에 있는 데이터끼리는 비슷한 특성을 갖는다. 이 특성을 이용해 각 칼럼마다 최적의 압축과 인코딩 방식을 선택하면 공간을 절약할 수 있다.
2. 압축과 인코딩
압축
압축은 데이터를 저장할 때 공간 효율성을 높이고 디스크 I/O를 줄이는 역할을 한다.
Parquet에서는 SNAPPY, GZIP, LZO 등의 압축 방식을 제공한다.
인코딩
인코딩은 압축과 함께 공간 효율성을 높이고 읽기 성능을 향상시키는 목적을 갖는다.
Parquet에서는 Dictionary Encoding, Run-Length Encoding, DELTA Encoding 등의 인코딩 방식을 제공한다.
이전에 진행했던 프로젝트 Javascript Code Golf에서 사용했던 개념들을 찾아볼 수 있었다.
Dictionary Encoding
자주 사용되는 값들의 Dictionary를 만들어놓고 해당 값을 참조하여 사용한다.
반복되는 문자열 데이터가 많은 경우에 적합하다.
dict = {
0: "Seoul"
1: "New York",
2: "Paris",
}
# ["Seoul", "Seoul", "Seoul", "New York", "Paris", "New York"]
data = [0,0,0,1,2,1]
Run-Length Encoding(RLE)
값과 그 값이 반복된 횟수를 저장한다.
연속적으로 반복되는 데이터가 많은 경우 적합하다.
# [10,10,20,20,20,30]
data = [(10,2),(20,3),(30,1)]
DELTA Encoding
데이터의 변화량을 저장한다.
시계열 데이터에 적합하다.
# [100,101,102,99,103]
data = [100,1,1,-3,4]
3. 구성 요소
Parquet는 Vertical Partition과 Horizontal Partition 개념을 모두 사용한다.
전체 데이터 로우를 수평으로 나눈 단위를 Row Group,
이를 각 필드(ID, Name, Age)를 기준으로 수직으로 나눈 단위를 Column Chunk,
이를 다시 수평으로 나눈 단위를 Page 라고 한다.
ID,Name,Age
// Row Group 1
1,2,3 // Column Chunk 1
John,Kim,Park // Column Chunk 2
30,24,24 // Column Chunk 3
// Row Group 2
4,5,6 // Column Chunk 4
Lee,Alice,Bob // Column Chunk 5
15,28,28 // Column Chunk 6
Row Group은 MapReduce 작업의 단위로, 여러 노드에서 병렬적으로 처리하기 위함이다. 512MB ~ 1GB가 권장사항으로 HDFS 블록 사이즈와 맞추길 권장한다.
Column Chunk는 각 노드에서 이루어지는 I/O의 단위이다. 위에서 설명한 칼럼 단위 데이터 저장 방식의 이점을 갖는다.
Page는 Column Chunk를 쪼갠 것으로, 인코딩과 압축이 이루어지는 단위가 된다. 권장사항은 8KB이다.
Column Chunk 단위로 인코딩과 압축을 하면 되는데 왜 굳이 Page로 다시 나눴을까?
1. 전체 Column Chunk를 인코딩/압축 하기엔 데이터가 여전히 많다.
하나의 row을 읽을 때에도 전체 청크를 다 디코딩해야된다고 한다면 무척 비효율적일 것이다.
2. 같은 타입의 데이터라도 분포에 따라 최적의 인코딩 방식이 다를 수 있다.
이 외에도 Column Chunk 내에서 빠른 데이터 접근을 위한 인덱싱을 위해 나눴다고 볼 수도 있을 것 같다.
4. 데이터 타입
Parquet는 아래와 같은 원시 타입을 제공한다. "물리적으로 어떻게 저장할지"를 나타낸다.
- BOOLEAN: 1 bit boolean
- INT32: 32 bit signed ints
- INT64: 64 bit signed ints
- INT96: 96 bit signed ints
- FLOAT: IEEE 32-bit floating point values
- DOUBLE: IEEE 64-bit floating point values
- BYTE_ARRAY: arbitrarily long byte arrays
- FIXED_LEN_BYTE_ARRAY: fixed length byte arrays
Logical Type(논리 타입)은 이를 확장하여 좀 더 고수준의 데이터 타입을 제공한다. "저장된 데이터를 어떻게 해석할지"를 나타낸다. String, Signed/Unsigned Integer, Date 등 다양한 타입을 지원한다.
5. 메타데이터
Parquet에서 메타데이터는 크게 FileMetaData와 Page header로 나뉜다.
FileMetaData는 parquet 파일 가장 뒤에 위치하여 파일 정보, 스키마 정보(이름, 데이터 타입 등), Row Group 정보, Column Chunk 정보등이 저장되어 있다.
Page header는 각 페이지 데이터의 앞에 저장되어 있고 페이지 크기, 인코딩 정보, 인덱싱을 위한 정보 등이 저장되어 있다.
왜 parquet는 메타데이터를 뒤에 둘까?
사실 메타데이터의 위치는 파일마다 다르다. parquet에 한정되는 내용은 아니고, 일반적으로 뒤에 두었을 때의 이점은 메타데이터가 변경되었을 때이다.
맨 앞에 메타데이터를 두었다가 메타데이터의 길이가 변경되면 그 뒤 데이터를 전부 다시 써야하는 상황이 올 수도 있기 떄문이다.
Paruqet는 데이터를 가공하기 전, 운영환경에서 수집한 데이터를 파일로 보관할 때 사용된다고 한다.
인덱싱같은 자세한 내부 동작은 정리하지 않았지만 무엇을 위한 것이고 전체적으로 어떻게 생겨먹은 파일인지만 이해해도 충분할 것 같다.
- Total
- Today
- Yesterday