Parquet์—์„œ ์ปฌ๋Ÿผ ๋ฐ์ดํ„ฐ๊ฐ€ ์ €์žฅ๋˜๋Š” ์ตœ์†Œ ๋‹จ์œ„, Page

8 minute read

๋ฐ์ดํ„ฐ ์—”์ง€๋‹ˆ์–ด๋กœ์„œ ์ผ์„ ํ•œ์ง€ ๋ฒŒ์จ 3๋…„์ด ๋„˜์—ˆ๋‹คโ€ฆ! ๊ทธ๋Ÿฐ๋ฐ ๊ทธ๋™์•ˆ ๋งจ๋‚  ์จ์˜จ .parquet ํฌ๋งท์„ ๋‚ด๊ฐ€ ์ œ๋Œ€๋กœ ์•Œ๊ณ  ์žˆ๋Š”๊ฒŒ ๋งž๋‚˜?๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค. Parquet๋„ ํฌ๋งท์ด v2๊นŒ์ง€ ๋‚˜์˜จ ๊ฝค ์˜ค๋ž˜๋˜๊ณ  ์œ ์„œ ๊นŠ์€ ํฌ๋งท์ด๊ณ , ๋˜ ์ตœ๊ทผ์—๋Š” Parquet Variant ํƒ€์ž… ๊ด€๋ จํ•ด์„œ๋„ ์ž‘์—…์„ ํ•˜๊ณ  ์žˆ๋Š”๋ฐ, ๋‚ด๊ฐ€ ์ด ํฌ๋งท์— ๋Œ€ํ•ด ์ž์‹  ์žˆ๊ฒŒ ์–˜๊ธฐํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์Šค์Šค๋กœ ๋Œ์•„๋ณด๊ฒŒ ๋˜์—ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ด๋ฒˆ ๊ธฐํšŒ์— Parquet๋ฅผ ๋‹ค์‹œ ๋ณด๊ณ  ๋‚ด๊ฐ€ ๋ถ€์กฑํ–ˆ๋˜ ๋ถ€๋ถ„๋“ค์€ ์ฑ„์›Œ๋„ฃ์–ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

์ง€๋‚œ ํฌ์ŠคํŠธ์—์„  Parquet ํฌ๋งท์˜ ๊ตฌ์กฐ๋ฅผ ์‚ดํŽด๋ณด์•˜๋‹ค. ์ด๋ฒˆ ํฌ์ŠคํŠธ๋Š” Parquet์—์„œ ์ปฌ๋Ÿผ ๋ฐ์ดํ„ฐ๊ฐ€ ์ €์žฅ๋˜๋Š” ์ตœ์†Œ ๋‹จ์œ„์ธ Page ๊ตฌ์กฐ์— ๋Œ€ํ•ด ์‚ดํŽด๋ณธ๋‹ค.

alt text

Dictionary Page

์ปฌ๋Ÿผ ๋ฐ์ดํ„ฐ๋ฅผ Parquet์— ์ €์žฅํ•  ๋•Œ, ๋ฐ์ดํ„ฐ์˜ ์นด๋””๋„๋ฆฌํ‹ฐ์— ๋”ฐ๋ผ์„œ Dictionary Encoding ํ•˜๋Š” ๊ฒฝ์šฐ๋„ ์žˆ๋‹ค. ์นด๋””๋„๋ฆฌํ‹ฐ๊ฐ€ ๋‚ฎ๋‹ค๋ฉด Dictionary Encoding ๋œ๋‹ค.

๋ชจ๋“  primitive ํƒ€์ž…์€ Dictionary Encoding์ด ๊ฐ€๋Šฅํ•˜๋‹ค. Parquet write๋Š” ์ฒ˜์Œ์—๋Š” Dictionary Encoding์œผ๋กœ ๋ฐ์ดํ„ฐ ์“ฐ๊ธฐ๋ฅผ ์‹œ๋„ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‹ค๊ฐ€ ๊ณ ์œ ๊ฐ’์ด ๋„ˆ๋ฌด ๋งŽ์•„์ ธ์„œ Dictionary Page ํฌ๊ธฐ๊ฐ€ ์ผ์ • ํฌ๊ธฐ ์ด์ƒ์œผ๋กœ ์ปค์ง€๋ฉด, ๊ทธ๋•Œ๋Š” Dictionary Encoding์„ ํฌ๊ธฐํ•˜๊ณ  ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ์ „ํ™˜ํ•œ๋‹ค.

spark์—์„œ๋Š” parquet.dictionary.page.size = 1mb ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์„ธํŒ… ๋˜์–ด ์žˆ๊ณ , ์š” config๋กœ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ํฌ๊ธฐ๋ฅผ ๋„˜์œผ๋ฉด, ํ•ด๋‹น Column Chunk์—” Dictionary Page ์—†์ด Data Page๋งŒ ์กด์žฌํ•œ๋‹ค. ์ƒ๊ฐํ•ด๋ณด๋ฉด, Column Chunk์˜ ์‚ฌ์ด์ฆˆ๋„ 1mb๋กœ ์ œํ•œ๋˜๊ธฐ ๋•Œ๋ฌธ์—, Dictionary Page์˜ ์ตœ๋Œ€ ํฌ๊ธฐ๋„ 1mb๋กœ ์ œํ•œ๋˜๋Š”๊ฒƒ์ด ์ž์—ฐ์Šค๋Ÿฝ๊ธด ํ•˜๋‹ค! ํ•˜์ง€๋งŒ, Data Page์™€ Dictionary Page๋Š” ์„œ๋กœ ๋…๋ฆฝ์ ์ธ ์„ค์ •์ด๋‹ค. ๋‘ Page์˜ ์‚ฌ์ด์ฆˆ limit์ด ๊ฐ™์€๊ฒŒ ์˜๋„๋œ ์„ธํŒ…์ด๊ฒ ์ง€๋งŒ, ์‹ค์ œ ๋™์ž‘ํ•  ๋•Œ๋Š” ์„œ๋กœ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•œ๋‹ค๋Š” ๊ฒƒ! (์ œ๋Œ€๋กœ ์ ์€๊ฑด๊ฐ€..!?)

  • parquet.block.size -> Row Group์˜ ํฌ๊ธฐ (default: 128mb)
  • parquet.page.size -> Data Page์˜ ํฌ๊ธฐ ์ œํ•œ (default: 1mb)
  • parquet.dictionary.page.size -> Dictionary Page์˜ ํฌ๊ธฐ ์ œํ•œ (default: 1mb)

์ปฌ๋Ÿผ ๋ฐ์ดํ„ฐ์˜ ๊ฐ’์€ Row Group์œผ๋กœ ๋‚˜๋ˆ ์„œ ์ €์žฅ๋œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ Row Group์œผ๋กœ ๋ถ„ํ• ๋œ ์ปฌ๋Ÿผ ๋ฐ์ดํ„ฐ๋งˆ๋‹ค ์นด๋””๋„๋ฆฌํ‹ฐ๊ฐ€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ, Row Group 0์—์„œ๋Š” Column Chunk์— Diciontary Encoding์ด ์ ์šฉ๋˜์ง€๋งŒ, Row Group 1์—์„  fallback -> plain encoding์œผ๋กœ ์ €์žฅ๋˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.


Row Group(์‹ค์งˆ์ ์œผ๋ก  Chunk Column) ๋‚ด์—์„œ ํ•ด๋‹น ์ปฌ๋Ÿผ์ด Dictionary Encoding์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด, Chunk Column์€ ์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑ๋œ๋‹ค.

Dictionary Page -> Data Page 0 -> Data Page 1 -> โ€ฆ -> Data Page N

Definition Level

์š”๊ฑด Page์— ์ €์žฅํ•œ ๋ฐ์ดํ„ฐ์— null ๊ฐ’์ด ์–ด๋””์–ด๋””์— ์žˆ๋Š”์ง€ ํ‘œ์‹œํ•˜๋Š” bit array๋‹ค. Parquet Page ๊ตฌ์กฐ์—์„œ ์ œ์ผ ์‰ฌ์šด ๊ฐœ๋…์ด๋‹ค. ใ…‹ใ…‹

N๋ฒˆ์งธ ์œ„์น˜์˜ ๋ฐ์ดํ„ฐ๊ฐ€ null์ด๋ƒ ์•„๋‹ˆ๋ƒ๋ฅผ ๋ณธ๋‹ค. ๊ทธ๋ž˜์„œ null ๊ธฐ๋ฐ˜ ํ•„ํ„ฐ๋ฅผ ํ•  ๋•Œ, null count๋ฅผ ํ•  ๋•Œ๋Š” ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฅผ ์•ˆ ์ฝ๊ณ  Definition Level์„ ๋ณด๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ์ ‘๊ทผํ•˜๊ณ  null count๋ฅผ ์„ธ๋ฉด ๋œ๋‹ค.

Repetition Level

๋ชจ๋“  ๊ฐ’์ด flatํ•œ primitive ํƒ€์ž…์—์„œ๋Š” repetition level์ด ์ „๋ถ€ zero bit์ด ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์˜๋ฏธ๊ฐ€ ์—†๋‹ค. rep level์ด ์˜๋ฏธ๊ฐ€ ์žˆ์œผ๋ ค๋ฉด, ARRAY, STRUCT, VARIANT ํƒ€์ž…์ผ ๋•Œ๋‹ค.

ARRAY

์˜ˆ๋ฅผ ๋“ค์–ด, tags ARRAY<STRING> ์ปฌ๋Ÿผ์ด ์žˆ๊ณ , ๊ทธ ๊ฐ’์ด ์•„๋ž˜์™€ ๊ฐ™๋‹ค๊ณ  ํ•˜์ž.

row 0: tags = ["a", "b", "c"]
row 1: tags = ["d"]

์ด๊ฑธ flatํ•˜๊ฒŒ ํŽผ์น˜๋ฉด,

values:   a b c d
rep:      0 1 1 0
def:      1 1 1 1

์ด์ œ ๋ฌด์Šจ ์˜๋ฏธ์ผ๊นŒ? ์‹ถ๊ฒ ์ง€๋งŒ, ์ผ๋‹จ rep level์˜ ํŒจํ„ด์— ์ง‘์ค‘ํ•˜๋ผ. rep bit = 0์ด๋ฉด, ์ƒˆ๋กœ์šด ๋ฐฐ์—ด์˜ ์‹œ์ž‘ ๊ฐ’์ด๋ผ๋Š” ๊ฑฐ๋‹ค. ์ฆ‰, ๋ฐฐ์—ด์„ ์‹œ์ž‘ํ•˜๋Š” ๊ด„ํ˜ธ ( ๋˜๋Š” [๋ผ๋Š” ๊ฒƒ์ด๋‹ค. ๋ฐ˜๋Œ€๋กœ rep bit = 1์ด๋ฉด, ์ด์ „์— ์—ด์—ˆ๋–ค ๋ฐฐ์—ด์ด ๊ณ„์†๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์ฆ‰, ์ฝค๋งˆ ,๋ผ๋Š” ๊ฒƒ์ด๋‹ค.

์ด์ œ ์ด ๋‚ด์šฉ์„ ๊ฐ€์ง€๊ณ  ์œ„์˜ ํ‘œํ˜„์„ ๋‹ค์‹œ ์ ์–ด๋ณด์ž.

values:   a b c d
rep:      0 1 1 0
rep*:     [ , , ][...

empty array, null array

row 0: tags = ["a", "b", "c"]
row 1: tags = []
row 2: tags = ["d"]

๋ฐ์ดํ„ฐ ์‚ฌ์ด์— empty array []๊ฐ€ ๋ผ์–ด๋“  ์ผ€์ด์Šค๋ฅผ ๋ณด์ž. ์ด๋•Œ, def level์ด ๋นˆ๋ฐฐ์—ด๊ณผ null ๋ฐฐ์—ด์„ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•ด ์ด๋ ‡๊ฒŒ ์ •์˜๋œ๋‹ค.

  • def=0: tags ์ž์ฒด๊ฐ€ nulll
  • def=1: tags๋Š” ์กด์žฌํ•˜์ง€๋งŒ ๋น„์–ด์žˆ์Œ. empty array
  • def=2: tags์— ๊ฐ’์ด ์žˆ์Œ.

๊ทธ๋ž˜์„œ ํ‘œํ˜„์‹์ด ์ด๋ ‡๊ฒŒ ๋œ๋‹ค.

values:   a b c (empty) d
rep:      0 1 1 0       0
def:      2 2 2 1       2

Nested Array

์ด๋ฒˆ์—๋Š” ๋ฐฐ์—ด์ธ๋ฐ, ์ค‘์ฒฉ ๋ฐฐ์—ด์ด๋‹ค. items ARRAY<ARRAY<STRING>> ์ปฌ๋Ÿผ์ด ์žˆ๋‹ค๊ณ  ํ•˜์ž. ์ด ์ปฌ๋Ÿผ์€ ์•„์ดํ…œ ๋ชฉ๋ก์„ ๋‹ด๋Š”๋ฐ, ๊ทธ ์•ˆ์—๋Š” ๊ทธ ์•„์ดํ…œ์ด ๊ฐ€์ง„ tag ์ •๋ณด๋ฅผ ๊ฐ–๋Š”๋‹ค.

row 0: items = [ {tags: ["a", "b"]}, {tags: ["c"]}]
row 1: items = [ {tags: []} ]
row 2: items = [ {tags: ["d"]} ]
row 3: items = []
row 4: items = null

def level์€ ์ด๋ ‡๊ฒŒ ์ •์˜๋œ๋‹ค.

  • def=0 -> ์•„์ดํ…œ ์ž์ฒด๊ฐ€ null -> null
  • def=1 -> items๋Š” ์žˆ์ง€๋งŒ, ๋น„์–ด์žˆ์Œ. -> []
  • def=2 -> items๋Š” ์žˆ์ง€๋งŒ, tags๊ฐ€ ๋น„์–ด์žˆ์Œ. -> [[]]
  • def=3 -> items๋„ ์žˆ๊ณ , tag์—๋„ ๊ฐ’์ด ์žˆ์Œ. -> [["a"]]

rep level์€ ์ด๋ ‡๊ฒŒ ์ •์˜๋œ๋‹ค.

  • rep=0 -> ์ƒˆ row์˜ ์‹œ์ž‘ -> item์„ ์—ฌ๋Š” ๊ด„ํ˜ธ [
  • rep=1 -> ๊ฐ™์€ row ์•ˆ์—์„œ ์ƒˆ item์ด ์‹œ์ž‘๋œ๋‹ค. -> tags๋ฅผ ์—ฌ๋Š” ๊ด„ํ˜ธ [ {tags:
  • rep=2 -> ๊ฐ™์€ item ์•ˆ์—์„œ tags ๊ฐ’์ด ๊ณ„์† ์ด์–ด์ง.

์ด์ œ ์ •์˜๋Œ€๋กœ rep/dev level๋กœ ํ‘œํ˜„ํ•˜๋ฉด,

value:  a  b  c  [[]]  d  []  null
rep:    0  2  1  0     0  0   0
def:    3  3  3  2     3  1   0

STRUCT

์ด๋Ÿฐ ์Šคํ‚ค๋งˆ๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•˜์ž.

message Order {
  optitional group address {
    required string city;
    optional string zipcode;
  }
}

๊ทธ๋ฆฌ๊ณ  ๋ฐ์ดํ„ฐ๋Š”

row 0: address = {city: "Seoul", zipcode: "04524"}
row 0: address = {city: "Pohang", zipcode: null}
row 0: address = null

STRUCT ํƒ€์ž…์€ leaf ์ปฌ๋Ÿผ์œผ๋กœ ๋ถ„ํ•ดํ•ด์„œ ์ €์žฅํ•œ๋‹ค. ์ฆ‰, address.city, address.zipcode๊ฐ€ ๋ณ„๋„์˜ Column Chunk๋กœ ์ €์žฅ๋œ๋‹ค. ๊ทธ๋ž˜์„œ STRUCT ํƒ€์ž…์€ ๊ตฌ์กฐ์ฒด ์ปฌ๋Ÿผ์ž„์—๋„ ๋น ๋ฅธ ์ฟผ๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•œ ๊ฒƒ์ด๋‹ค.

def/rep level๋„ ๊ฐ leaf ์ปฌ๋Ÿผ ๊ธฐ์ค€์œผ๋กœ ์ •ํ•ด์ง„๋‹ค. ์š”๊ฑด primitive ํƒ€์ž…๊ณผ ๋น„์Šทํ•˜๊ฒŒ ์ด๋ค„์ง€๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ๊ธฐ์—์„œ๋Š” ์ƒ๋žต ํ•˜๊ฒ ๋‹ค.

์ด๋ ‡๊ฒŒ STRUCT ํƒ€์ž…์„ leaf ์ปฌ๋Ÿผ์œผ๋กœ ๋ถ„ํ•ดํ•ด์„œ ๊ฐ๊ฐ ์ €์žฅํ•˜๋Š” ๊ฑธ โ€œShreddingโ€œ์ด๋ผ๊ณ  ํ•œ๋‹ค. address ์ปฌ๋Ÿผ ์ž์ฒด๋Š” Column Chunk๊ฐ€ ์—†์œผ๋ฉฐ, STRUCT ๊ตฌ์กฐ ์ •๋ณด๋Š” Parquet ์Šคํ‚ค๋งˆ์—๋งŒ ์กด์žฌํ•œ๋‹ค.

๋ฐ˜๋Œ€๋กœ STRUCT ํƒ€์ž…์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๋Š” ๊ณผ์ •์€ Assembly๋ผ๊ณ  ํ•œ๋‹ค. ๋ถ„ํ•ด๋œ leaf ์ปฌ๋Ÿผ์„ ์›๋ž˜์˜ STRUCT ๊ตฌ์กฐ๋กœ ๋ณต์›ํ•˜๋Š” ๊ฒƒ!

DATA_PAGE_V2

Parquet 1.x ํ›„๋ฐ˜์— ์ถ”๊ฐ€๋œ ์ด ํฌ๋งท์€ Data Page๋ฅผ ์ €์žฅํ•˜๊ณ  ์••์ถ•ํ•  ๋•Œ, rep/def level์„ ์••์ถ•ํ•˜์ง€ ์•Š๋Š”๋‹ค.

๋ณธ๋ž˜ DATA_PAGE์—์„  rep+def+values ์ „์ฒด๋ฅผ ์••์ถ•ํ•ด์„œ ์ €์žฅํ–ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ, ์ด๋ ‡๊ฒŒ ํ•˜๋‹ˆ null ๊ฐ’์„ ์ฐพ์„ ๋•Œ, Page ์ „์ฒด๋ฅผ ์••์ถ• ํ•ด์ œํ•ด์•ผ ๊ทธ ์•ˆ์˜ def level์„ ๋ณด๊ณ  null ๊ฐ’ ํ•„ํ„ฐ๋ง์ด ๊ฐ€๋Šฅํ–ˆ๋‹ค.

DATA_PAGE_V2๋Š” ์˜ค์ง values ๊ฐ’๋งŒ ์••์ถ•ํ•ด์„œ ์ €์žฅํ•œ๋‹ค. rep/def level์€ ์••์ถ•ํ•˜์ง€ ์•Š๊ณ  ๊ทธ๋Œ€๋กœ ์ €์žฅํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด, null ํ•„ํ„ฐ๋ง์„ ์••์ถ•ํ•ด์ œ ์—†์ด ๋น ๋ฅด๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.

Spark์—์„œ๋Š” ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ DATA_PAGE(v1)์„ ์‚ฌ์šฉํ•œ๋‹ค. v2๋ฅผ ์“ฐ๋ ค๋ฉด, spark.sql.parquet.dataPageVersion=v2๋กœ ๋ช…์‹œํ•ด์ค˜์•ผ ํ•œ๋‹ค.

Updated: