본문 바로가기

IT/데이터

Parquet 컬럼 기반 저장 방식의 특징 및 데이터 추출 원리 및 로우(Row) 기반과 성능 비교

반응형

컬럼 기반 Parquet 특징 및 성능 비교

✅ 컬럼(Columnar) 기반 저장 특징

  • 데이터를 열 단위로 저장하여 분석 쿼리 성능이 빠름
  • 컬럼별 압축률이 우수함
  • 디스크에서 읽는 데이터의 양을 최소화 (필요한 컬럼만 읽음)
  • 분석 및 집계 처리에 최적화 (OLAP 환경에 적합)

✅ 로우(Row) 기반 저장 특징

  • 데이터를 행 단위로 저장하여 트랜잭션 처리에 최적화
  • 특정 행 전체를 자주 조회할 경우 유리 (OLTP 환경에 적합)
  • 그러나 분석용 쿼리에는 불필요한 컬럼까지 읽으므로 성능 저하

Parquet의 컬럼 저장 구조 예시

다음과 같은 테이블이 있다고 가정하겠습니다.

user_id product_id purchase_date price
1 1001 2025-03-01 3000
1 1005 2025-03-02 5000
2 1003 2025-03-01 4500
3 1002 2025-03-03 2000


컬럼 기반 Parquet 저장 방식 

반면, Parquet은 아래처럼 각 컬럼을 독립적으로 저장합니다.

[user_id] →  [1, 1, 2, 3]
[product_id] → [1001, 1005, 1003, 1002]
[purchase_date] → ["2025-03-01", "2025-03-02", "2025-03-01", "2025-03-03"]
[price] → [3000, 5000, 4500, 2000]
  • 데이터를 각 컬럼 단위로 묶어서 저장합니다.
  • 컬럼별로 동일한 데이터 타입이기 때문에 압축 효율이 매우 높습니다.
  • 특정 컬럼만 읽을 수 있으므로, 조회 대상이 아닌 컬럼은 읽을 필요가 없어 성능이 향상됩니다.

Parquet 저장의 세부적인 내부 구조

Parquet 파일은 내부적으로 Row GroupColumn ChunkPage라는 단위로 저장됩니다.

Parquet 파일
 ├─ Row Group 1
 │   ├─ Column Chunk (user_id)
 │   │   └─ [Page: 1, 1, 2, 3]
 │   ├─ Column Chunk (product_id)
 │   │   └─ [Page: 1001, 1005, 1003, 1002]
 │   ├─ Column Chunk (purchase_date)
 │   │   └─ [Page: "2025-03-01", "2025-03-02", ...]
 │   └─ Column Chunk (price)
 │       └─ [Page: 3000, 5000, 4500, 2000]
 │
 └─ Row Group 2 (더 많은 데이터가 있을 경우)
  • Row Group
    • Parquet 파일 내 데이터를 나누는 큰 단위입니다.
    • 일반적으로 수만~수십만 레코드 단위로 나눕니다.
  • Column Chunk
    • 각 Row Group 내에서 컬럼별로 데이터를 저장한 단위입니다.
  • Page
    • Column Chunk 내 데이터를 더 작게 나눈 블록 단위이며, 압축이나 인코딩이 적용되는 최소 단위입니다.
반응형

특정 사용자 데이터 추출 시 성능이 좋은 이유

예를 들어, 다음 쿼리를 생각해 보겠습니다

SELECT * FROM table WHERE user_id = 1

 
Parquet에서의 데이터 접근 프로세스

  1. 컬럼 선택 (Column Pruning)
     user_id 컬럼부터 읽고, 조건(user_id=1)에 해당하는 레코드의 위치를 확인합니다.
  2. 위치 기반 데이터 추출
     조건에 맞는 데이터의 위치(첫 번째, 두 번째 행)를 찾으면, 동일한 위치의 나머지 컬럼만 추가로 읽습니다.

즉, 모든 컬럼을 다 읽지 않아도 되기 때문에 Parquet은 로우 기반 대비 훨씬 빠르게 특정 데이터를 추출할 수 있습니다.


Parquet에서 컬럼 간 데이터 연관 방식

Parquet은 행(row) 위치를 기반으로 컬럼 간 데이터를 연결합니다.
예시 데이터를 다시 보면 다음과 같습니다

행번호 (row index) user_id product_id purchase_date price
0 1 1001 2025-03-01 3000
1 1 1005 2025-03-02 5000
2 2 1003 2025-03-01 4500
3 3 1002 2025-03-03 2000

컬럼 방식으로 저장하면 아래와 같습니다.

[user_id] 컬럼 블록 → [1, 1, 2, 3]
[product_id] 컬럼 블록 → [1001, 1005, 1003, 1002]
[purchase_date] 컬럼 블록 → ["2025-03-01", "2025-03-02", "2025-03-01", "2025-03-03"]
[price] 컬럼 블록 → [3000, 5000, 4500, 2000]
  • 각 컬럼의 데이터는 동일한 순서(행 순서)로 저장됩니다.
  • 즉, 같은 행에 있는 데이터는 각 컬럼에서 동일한 위치(인덱스)에 위치하게 됩니다.

 user_id = 1과 product_id 간의 연관성

예시 쿼리를 다시 보면

SELECT product_id FROM table WHERE user_id = 1;

다음과 같이 추출됩니다.

 처리 과정

  1. user_id 컬럼을 읽음
[1, 1, 2, 3]
 ↑  ↑
0번, 1번 인덱스가 조건(user_id=1)에 부합

이후 같은 인덱스의 데이터를 product_id 컬럼에서 읽음

[1001, 1005, 1003, 1002]
  ↑     ↑
 0번   1번

컬럼 기반 저장에서 연관성을 유지하는 원리 

  • 컬럼 기반 저장은 별도의 레코드 ID나 명시적인 포인터(pointer)가 아니라, 데이터의 위치(index)를 통해 컬럼 간 데이터를 연결합니다.
  • Parquet 파일 내부에서 각 컬럼은 독립적으로 저장되지만, 데이터의 순서가 유지됩니다.
    (즉, 1번째 user_id 값은 1번째 product_id 값과 연관되는 구조)
  • 따라서 연관성은 항상 컬럼끼리 동일한 위치(index)를 기준으로 유지됩니다.

컬럼 기반 Parquet에서 사용자 데이터 추출 프로세스 예시

 예시 상황

  • 컬럼: user_id, product_id, purchase_date, price
  • 목표: 사용자 아이디(user_id)가 1인 모든 구매 이력을 가져오기

 접근방법

① Parquet 파일을 저장한 스토리지에서 필요한 컬럼만 읽음 (predicate pushdown 활용)
② user_id 컬럼에 대한 필터를 적용하여 ID=1인 데이터만 스캔 (column pruning)
③ 필터된 결과를 추출하여 결과셋 생성

# PySpark를 활용한 예시 코드
df = spark.read.parquet("path/to/data.parquet")

result_df = df.filter(df.user_id == 1)  # Predicate pushdown 발생
result_df.show()

Parquet의 predicate pushdown이란?
조건절을 스토리지 레벨에서 미리 적용하여, 디스크 I/O와 네트워크 트래픽을 최소화하는 최적화 방식입니다.

로우 기반과 컬럼 기반 성능 비교 요약

항목 컬럼 기반(Parquet) 로우 기반(CSV, Avro 등)
조회 유형 분석, 필터링 및 집계에 유리 트랜잭션 기반 전체 행 조회에 유리
디스크 읽기 성능 필요한 컬럼만 읽어 속도가 빠름 전체 행을 읽어 속도 느림
압축 효율성 매우 우수 상대적으로 낮음
I/O 성능 높음 낮음
용도 데이터 분석, DW, OLAP 적합 OLTP 및 트랜잭션 처리에 적합

Apache Parquet, 컬럼 기반의 저장 방식

데이터가 폭발적으로 증가하는 시대, 우리는 데이터를 보다 효율적으로 저장하고 빠르게 분석할 수 있는 방법이 필요합니다. Apache Parquet은 이러한 요구를 충족하는 강력한 컬럼 기반 저장 형식(

make2t.tistory.com

 
 

반응형