Warehouse

Konsep data warehouse modern pada Snowflake, BigQuery, dan Redshift.

Daftar isi

Warehouse adalah tujuan utama data yang diproses pipeline, yaitu penyimpanan analitik tempat tabel besar dibaca oleh query yang berulang-ulang. Topik ini membahas tiga konsep yang menjadi ciri warehouse modern, yaitu pemisahan penyimpanan dan komputasi, tabel yang dikelola penuh oleh sistem, serta mikro-partisi dan kolom kluster. Snowflake, BigQuery, dan Redshift dibandingkan pada tingkat arsitektur, kemudian perilaku pemangkasannya didemonstrasikan dengan DuckDB pada data yang benar-benar dijalankan.

Pemisahan penyimpanan dan komputasi#

Konsep yang paling menentukan pada warehouse modern adalah pemisahan lapisan penyimpanan dari lapisan komputasi, sehingga keduanya diskalakan secara terpisah. Warehouse generasi terdahulu menyatukan keduanya pada satu kluster, sehingga menambah kapasitas penyimpanan berarti menambah mesin komputasi sekaligus, meskipun yang tumbuh sebenarnya hanya salah satunya. Snowflake menyimpan data pada penyimpanan objek cloud dan menyediakan virtual warehouse, yakni kelompok mesin komputasi yang dapat dibuat, dihentikan, atau digandakan tanpa memindahkan data sedikit pun. BigQuery menempuh jalan yang serupa dengan menyimpan tabel pada penyimpanan kolom terkelolanya dan menjalankan query pada slot yang dialokasikan untuk pekerjaan itu. Redshift, yang awalnya menyatukan penyimpanan dan komputasi pada setiap node kluster, kini menyediakan varian penyimpanan terkelola yang memisahkan keduanya.

Konsekuensi praktisnya terasa langsung pada pekerjaan sehari-hari. Dua unit komputasi dapat membaca tabel yang sama pada waktu yang sama tanpa saling berebut sumber daya, misalnya satu unit untuk transformasi terjadwal dan satu unit untuk analisis sesaat. Komputasi yang tidak dipakai dapat dimatikan sehingga tidak menimbulkan biaya, sedangkan penyimpanannya tetap utuh. Ketika sebuah query membutuhkan tenaga lebih besar, kapasitas komputasinya dapat dinaikkan sementara lalu dikembalikan.

Diagram berikut merangkum susunan tersebut. Satu penyimpanan terkelola dilayani oleh dua unit komputasi yang berdiri sendiri, dan tidak ada yang menyimpan data milik sendiri.

Pemisahan penyimpanan dan komputasi pada warehouse modernKomputasi transformasiunit komputasi pertamaKomputasi analisisunit komputasi keduaquery dan muat dataPenyimpanan terkelolatabel beserta metadata rentang nilai tiap partisinya
Pemisahan penyimpanan dan komputasi pada warehouse modern

Tabel yang dikelola warehouse#

Tabel pada warehouse modern dikelola penuh oleh sistem, sehingga pengelolaannya tidak lagi menyentuh berkas fisik sama sekali. Pengguna cukup mendeklarasikan tabel dengan SQL, lalu sistem yang menentukan tata letak fisiknya, membagi datanya menjadi unit penyimpanan, dan memelihara metadata beserta statistiknya. Operasi tulis ke tabel pun berupa SQL biasa, yaitu pernyataan INSERT, COPY, atau MERGE. Setiap operasi itu bersifat atomik, sehingga pembaca tidak pernah melihat tabel yang datanya baru dimuat sebagian.

Posisi penyimpanan objek pada susunan ini adalah sumber data mentah, bukan pengganti tabel. Warehouse modern menyediakan tabel eksternal (external table), yakni tabel yang metadatanya terdaftar pada warehouse sedangkan datanya tetap berupa berkas Parquet atau CSV di penyimpanan objek. Pola ini membuat data lake tetap menjadi arsip mentah, sementara tabel terkelola menjadi bentuk final yang dikonsumsi analitik.

Konsekuensi model ini adalah jaminan konsistensi yang tidak diberikan tata kelola berkas manual. Satu tabel memiliki definisi skema yang sah, statistik yang diperbarui otomatis untuk perencanaan query, dan transaksi yang menjaga pembacaan dari data yang belum selesai dimuat. Detail seperti itulah yang membedakan tabel warehouse dari sekadar kumpulan berkas di sebuah direktori.

Mikro-partisi dan kolom kluster#

Warehouse menyusun tabel ke dalam unit kecil bernama mikro-partisi (micro-partition), yaitu potongan data yang tak dapat diubah beserta metadata statistiknya. Setiap unit menyimpan rentang nilai minimum dan maksimum untuk kolom-kolom di dalamnya, sehingga query yang menyaring kolom tertentu dapat melewati unit yang pasti tidak memuat barisnya. Perilaku ini adalah pemangkasan partisi yang sama dengan yang bekerja pada dataset Parquet, tetapi di sini pemangkasannya berlangsung tanpa konfigurasi apa pun karena metadata sudah ditata oleh sistem.

Urutan data di dalam partisi menentukan efektivitas pemangkasan. Data yang masuk mengikuti urutan kedatangan sering sejalan dengan query yang menyaring tanggal, sehingga pemangkasan bekerja hampir otomatis. Apabila query justru sering menyaring kolom lain, misalnya wilayah atau jenis sensor, kolom kluster (clustering key) dapat ditetapkan, yakni kolom yang dipilih agar baris dengan nilai serupa ditempatkan pada unit yang sama.

Ketiga warehouse mengusung mekanisme yang sejajar. Snowflake menata mikro-partisi secara otomatis dan menyediakan kolom kluster opsional untuk tabel besar. BigQuery menyerahkan pemilihan partisi tabel dan kolom kluster kepada pengguna saat tabel didefinisikan, kemudian mengelola tata letaknya sendiri. Redshift meminta sort key dan dist key dinyatakan eksplisit, yakni kolom yang mengatur urutan fisik data dan kolom yang mengatur penyebarannya antar node.

Perbandingan Snowflake, BigQuery, dan Redshift#

Perbandingan berikut menempatkan ketiganya pada tingkat arsitektur, dan sengaja tidak memasukkan harga karena angkanya berubah dan bergantung pada penawaran yang berlaku. Yang dibandingkan adalah cara setiap produk menyimpan data, menyediakan komputasi, dan mengelola urutan fisiknya.

AspekSnowflakeBigQueryRedshift
Penyimpananpenyimpanan objek cloud yang dikelola Snowflakepenyimpanan kolom terkelola milik Googlepenyimpanan terkelola pada kluster (varian RA3)
Unit komputasivirtual warehouse per beban kerjaslot yang dibagi antar querynode komputasi pada kluster
Penskalaanbuat, hentikan, dan gandakan unit secara bebastambah kapasitas slot atau bayar per pemindaian queryubah jumlah node atau rasio penyimpanannya
Urutan fisikotomatis, dengan kolom kluster opsionaldideklarasikan per tabel (partitioning dan clustering)dideklarasikan eksplisit (sort key dan dist key)

Bacaan yang sehat dari tabel tersebut adalah kesamaan janjinya, bukan pemenangnya. Ketiganya menjanjikan penyimpanan yang dapat dipangkas, komputasi yang dapat diskalakan, dan SQL sebagai antarmukanya. Lapisan penyimpanan ketiganya juga berpindah ke penyimpanan terkelola cloud, sehingga pembeda utamanya ada pada lapisan komputasi. Perbedaan sisanya lebih banyak menyangkut siapa yang mengambil keputusan, yaitu sistem yang menata semuanya secara otomatis atau pengguna yang mendeklarasikannya sejak awal. Bagi pembelajar, konsep pada topik ini berpindah dari satu produk ke produk lain tanpa perlu memulai dari nol.

Demonstrasi pemangkasan dengan DuckDB#

Perilaku pemangkasan yang dijanjikan warehouse dapat diamati dengan DuckDB karena DuckDB menerapkan prinsip yang sama, yaitu penyimpanan kolom dengan metadata statistik per berkas. Skrip berikut menulis 9.000 baris pengukuran suhu menjadi 90 berkas Parquet, satu berkas per tanggal dengan pola kunci=nilai bergaya Hive. Setelah itu, skrip menjalankan query yang menyaring satu tanggal saja, lalu menampilkan rencana eksekusinya.

python demo-partisi-kolom.py
import os

import duckdb

con = duckdb.connect()

# 1. tabel pengukuran sensor suhu untuk 90 hari dengan 4 sensor
con.execute("""
    CREATE TABLE pengukuran AS
    SELECT date '2024-05-01' + (i % 90)::INTEGER AS tanggal,
           'S-' || (i % 4) AS sensor,
           20.0 + (i % 100) * 0.1 AS suhu_c
    FROM range(9000) t(i)
""")

# 2. tulis sebagai Parquet terpartisi, satu direktori per tanggal
con.execute("COPY pengukuran TO 'data' (FORMAT PARQUET, PARTITION_BY (tanggal))")

berkas = []
for akar, _, nama in os.walk("data"):
    berkas.extend(f"{akar}/{n}" for n in nama)
print("jumlah berkas parquet :", len(berkas))
print("contoh direktori partisi:")
for d in sorted(os.listdir("data"))[:3]:
    print("  -", d)

# 3. agregasi dengan filter tanggal literal
hasil = con.execute("""
    SELECT count(*), min(suhu_c), max(suhu_c)
    FROM read_parquet('data/**/*.parquet', hive_partitioning = 1)
    WHERE tanggal = DATE '2024-06-15'
""").fetchone()
print(f"baris 2024-06-15 : {hasil[0]}")
print(f"suhu terkecil    : {hasil[1]}")
print(f"suhu terbesar    : {hasil[2]}")

# 4. rencana eksekusi: berkas yang benar-benar disentuh query
rencana = con.execute("""
    EXPLAIN SELECT count(*), min(suhu_c), max(suhu_c)
    FROM read_parquet('data/**/*.parquet', hive_partitioning = 1)
    WHERE tanggal = DATE '2024-06-15'
""").fetchall()
print(rencana[0][1])
text Keluaran
jumlah berkas parquet : 90
contoh direktori partisi:
  - tanggal=2024-05-01
  - tanggal=2024-05-02
  - tanggal=2024-05-03
baris 2024-06-15 : 100
suhu terkecil    : 20.5
suhu terbesar    : 29.5
╭─ Ungrouped Aggregate ──────────╮
│ Aggregates: count_star(),      │
│             min(#0), max(#1)   │
│ ~1 row                         │
╰────────────────┬───────────────╯
╭─ Projection ───┴───────────────╮
│ Projections: suhu_c, suhu_c    │
│ ~1 row                         │
╰────────────────┬───────────────╯
╭─ Read Parquet ─┴───────────────╮
│ Function: READ_PARQUET         │
│ Projections: suhu_c            │
│ File Filters:                  │
│ tanggal = '2024-06-15'::DATE   │
│ Scanning Files: 1/90           │
│ ~1 row                         │
╰────────────────────────────────╯

Angka yang paling penting ada pada baris Scanning Files: 1/90. Meskipun query dinyatakan atas seluruh dataset, mesin eksekusinya hanya membuka satu berkas, karena filter tanggal literal memangkas 89 berkas lainnya melalui metadata partisinya. Kolom yang dibaca pun hanya suhu_c, seperti terlihat pada baris Projections, karena kolom lain tidak dibutuhkan agregasinya. Pada warehouse sungguhan mekanismenya serupa, hanya dengan jumlah partisi yang jauh lebih besar dan metadata yang dikelola sistem.

Kesalahan umum#

Kesalahan berikut paling sering muncul ketika konsep warehouse baru dipraktikkan, dan masing-masing menyertakan penyebab beserta perbaikannya.

  • Mengasumsikan setiap filter otomatis memangkas. Pemangkasan membutuhkan filter pada kolom partisi atau kolom kluster dengan nilai yang diketahui saat perencanaan, misalnya tanggal literal. Filter yang membungkus kolom partisi dengan fungsi sering menggagalkannya, sehingga saringan sebaiknya ditulis langsung pada kolom partisinya.
  • Memilih granularitas partisi yang terlalu halus. Partisi per menit pada data berkelanjutan menghasilkan banyak sekali berkas kecil, sehingga metadata membengkak dan query justru melambat. Pilih tingkatan yang sepadan dengan pola filter, biasanya per hari atau per bulan, lalu konsolidasikan berkas kecil secara berkala.
  • Memperlakukan warehouse sebagai basis data operasional. Pembaruan baris demi baris dan beban transaksi kecil bukan wilayahnya, dan beban semacam itu menimbulkan biaya komputasi besar tanpa manfaat analitik. Muat datanya secara batch, lalu jalankan transformasinya di dalam warehouse itu sendiri.

Ringkasan#

  • Warehouse modern memisahkan penyimpanan dari komputasi, sehingga banyak unit komputasi membaca data yang sama tanpa saling mengganggu dan komputasi yang menganggur tidak menimbulkan biaya.
  • Tabel terkelola menyembunyikan tata letak fisik dan menyediakan transaksi serta statistik otomatis, sedangkan tabel eksternal menjembatani data mentah di penyimpanan objek.
  • Mikro-partisi beserta metadata rentang nilainya memungkinkan pemangkasan partisi, yang diperkuat kolom kluster ketika pola filter tidak sejalan dengan urutan data.
  • Demonstrasi DuckDB menunjukkan prinsip yang sama, yaitu query dengan filter tanggal hanya menyentuh 1 dari 90 berkas Parquet.

Referensi#