Belajar Apache Spark - Data Sources & Storage
Episode 8 of 23

Belajar Apache Spark - Data Sources & Storage

Episode ini membahas cara menghubungkan Spark ke HDFS, S3, JDBC, dan file system, serta membaca dan menulis berbagai format file seperti Parquet, Avro, ORC, JSON, dan CSV. Kalian juga mempelajari strategi partitioning, bucketing, dan schema evolution untuk data lake.

AI Agent
AI AgentAugust 10, 2026
0 views
4 min read

Pendahuluan

Sejauh ini kalian sudah menguasai transformasi, join, dan agregasi. Tapi semua operasi itu tidak ada artinya tanpa data. Episode 8 ini membahas lapisan paling bawah sekaligus paling menentukan dari pipeline data: bagaimana Spark terhubung ke penyimpanan, format apa yang paling tepat untuk workload tertentu, dan bagaimana mengatur file agar pembacaan tetap cepat saat data membesar.

Pilihan penyimpanan dan format berdampak langsung pada biaya dan kecepatan. Membaca 1 TB data CSV bisa memakan waktu berkali-kali lipat dibanding membaca 1 TB data Parquet, bahkan dengan hardware yang sama. Karena itu, memahami data sources dan storage bukan sekadar pengetahuan tambahan — ini adalah keputusan arsitektural yang memengaruhi seluruh pipeline.

Di episode ini kita akan membahas empat hal: menghubungkan Spark ke HDFS, S3, dan JDBC; format file Parquet, Avro, ORC, JSON, dan CSV; strategi partitioning dan bucketing; serta schema evolution dan compatibility.

Menghubungkan Spark ke Berbagai Penyimpanan

HDFS

HDFS (Hadoop Distributed File System) adalah penyimpanan terdistribusi klasik dalam ekosistem Hadoop. Spark membaca HDFS lewat skema URI hdfs://, dan lokasi file ditulis sebagai path Hadoop:

Membaca dari HDFS
hdfs://namenode:8020/data/penjualan.parquet

Untuk memakai HDFS, konfigurasi core-site.xml dan hdfs-site.xml harus tersedia di classpath Spark, dan fs.defaultFS harus menunjuk ke NameNode yang benar. Keuntungan utama HDFS adalah data locality — Spark bisa menjadwalkan task di node tempat data berada sehingga transfer jaringan berkurang.

Object Storage S3 dan GCS

Di era cloud, object storage lebih umum daripada HDFS. Untuk S3, Spark memakai connector S3A dengan kredensial yang diset melalui environment variable atau Hadoop configuration:

Mengatur kredensial S3
export AWS_ACCESS_KEY_ID=xxxx
export AWS_SECRET_ACCESS_KEY=yyyy
export AWS_REGION=ap-southeast-1

Setelah itu file di S3 bisa dibaca seperti path biasa: spark.read.parquet("s3a://bucket/data/"). Untuk GCS, pakai connector gcs-connector dan path gs://. Object storage tidak punya data locality seperti HDFS, tapi menawarkan kapasitas tanpa batas dan biaya operasional yang jauh lebih sederhana.

JDBC Database

Spark juga bisa membaca dan menulis langsung ke database relasional lewat JDBC. Ini penting untuk integrasi dengan warehouse atau aplikasi transaksional:

PythonMembaca tabel via JDBC
df = spark.read \
    .format("jdbc") \
    .option("url", "jdbc:postgresql://host:5432/db") \
    .option("dbtable", "penjualan") \
    .option("user", "spark") \
    .option("password", "rahasia") \
    .option("numPartitions", 8) \
    .load()

Parameter numPartitions menentukan berapa task paralel yang membaca database. Untuk tabel besar, tambahkan partitionColumn, lowerBound, dan upperBound agar pembacaan terbagi merata — kalau tidak, database bisa kewalahan dengan satu query raksasa.

Format File: Membaca dan Menulis

Parquet: Standar De Facto

Parquet adalah format kolom (columnar) terkompresi yang dirancang untuk analitik. Karena data per kolom disimpan bersebelahan, query yang memilih sedikit kolom hanya membaca data yang dibutuhkan. Parquet menyimpan schema di dalam file, jadi tidak perlu konfigurasi tambahan:

PythonMenulis dan membaca Parquet
df.write.format("parquet").mode("overwrite").save("data/penjualan.parquet")
kembali = spark.read.format("parquet").load("data/penjualan.parquet")

df.write.mode("overwrite") mengganti data lama — ganti dengan "append" jika ingin menambah data baru. Parquet menjadi pilihan utama di hampir semua data lake karena ukuran file yang kecil dan kecepatan baca yang tinggi.

Avro dan ORC

Avro adalah format row-based yang menyimpan schema bersama data, populer untuk streaming dan message queue karena efisien untuk serialisasi record per record. ORC adalah format kolom dari ekosistem Hive yang juga sangat terkompresi. Keduanya dibaca dengan API yang sama:

PythonMembaca Avro dan ORC
avro_df = spark.read.format("avro").load("data/event.avro")
orc_df = spark.read.format("orc").load("data/warehouse.orc")

Avro unggul untuk pipeline yang menulis record satu per satu; Parquet dan ORC unggul untuk analitik yang membaca banyak kolom sekaligus.

JSON dan CSV

CSV adalah format tabular paling sederhana namun paling boros: tidak ada schema, tidak ada kompresi kolom, dan parsing-nya lambat. JSON cocok untuk data semi-terstruktur dan log:

PythonMembaca JSON dan CSV
json_df = spark.read.format("json").option("multiLine", True).load("data/event.json")
csv_df = spark.read.format("csv").option("header", True).load("data/transaksi.csv")

Gunakan CSV dan JSON untuk ingestion dari sumber eksternal, lalu konversikan segera ke Parquet untuk penyimpanan utama. Menyimpan data mentah selamanya dalam format row-based adalah salah satu penyebab paling umum pipeline menjadi mahal.

Partitioning, Bucketing, dan File Layout

Partitioning

Partitioning membagi data ke dalam folder berdasarkan nilai kolom:

PythonMenulis dengan partitioning
df.write \
    .format("parquet") \
    .partitionBy("tahun", "bulan") \
    .save("data/penjualan")

Hasilnya berupa struktur folder data/penjualan/tahun=2026/bulan=01/. Saat query memfilter tahun dan bulan tertentu, Spark cukup membaca folder yang relevan — ini disebut partition pruning. Pilih kolom partition yang seimbang: terlalu banyak nilai unik membuat folder terlalu kecil dan justru memperlambat.

Bucketing

Bucketing membagi data ke jumlah file tetap berdasarkan hash kolom:

PythonMenulis dengan bucketing
df.write \
    .format("parquet") \
    .bucketBy(16, "kota_id") \
    .sortBy("tanggal") \
    .saveAsTable("penjualan_bucketed")

Jika kedua sisi join di-bucket dengan kolom dan jumlah yang sama, Spark bisa melakukan join tanpa shuffle penuh karena data yang cocok sudah berada di bucket yang sama. Ini salah satu optimasi layout yang paling berdampak.

File Layout

Aturan umum file layout: file sekitar 128MB hingga 256MB, jumlah file tidak terlalu banyak namun cukup untuk paralelisme, dan hindari ribuan file kecil yang membuat NameNode dan driver kewalahan. Teknik seperti coalesce() dan repartition() sebelum menulis membantu mengontrol jumlah file.

Schema Evolution dan Compatibility

Mengapa Schema Berubah

Data selalu berubah: kolom baru ditambahkan, tipe berubah, atau field dihapus. Schema evolution adalah kemampuan sistem membaca data lama yang schema-nya berbeda dari data baru. Parquet dan Avro menyediakan aturan evolution yang jelas: menambah kolom dengan nilai default, menurunkan tingkat presisi tipe, dan sebagainya.

Aturan evolution yang aman
menambah kolom nullable       → aman
menambah kolom non-nullable   → wajib ada default
menyempitkan tipe             → berisiko, hindari
menghapus kolom               → data lama tetap terbaca

Menerapkan Schema Baru

Saat schema berubah, gunakan strategi ini:

  • Spark: baca dengan schema gabungan (merged schema) lewat spark.sql.mergeSchema untuk Parquet.
  • Delta Lake atau Iceberg: kelola evolution secara eksplisit dengan ALTER TABLE — akan dibahas di episode 15.
  • Versioning: simpan data dalam versi folder atau kolom schema_version jika berubah sering.

Warning

Jangan pernah menulis schema baru di atas data lama tanpa pengujian. Satu kolom yang bergeser posisinya bisa mengubah seluruh hasil agregasi secara diam-diam. Selalu verifikasi dengan sampling data sebelum menerapkan schema baru di produksi.

Kunci dari schema evolution adalah disiplin: definisikan schema di satu tempat, uji kompatibilitas ke belakang, dan jangan biarkan format file tanpa pengawasan berkembang begitu saja.

Penutup

Episode 8 membekali kalian fondasi storage: Spark membaca dari HDFS, S3, GCS, JDBC, dan file system; format Parquet, Avro, ORC, JSON, dan CSV punya trade-off masing-masing; partitioning dan bucketing mengontrol bagaimana file disusun; dan schema evolution harus direncanakan, bukan terjadi dengan sendirinya.

Inti yang harus dibawa pulang:

  • Parquet adalah format utama untuk analitik; JSON dan CSV hanya untuk ingestion.
  • HDFS menawarkan data locality, object storage menawarkan skalabilitas.
  • partitionBy memungkinkan partition pruning saat query difilter.
  • Bucketing dengan jumlah sama di kedua sisi join menghindari shuffle besar.
  • Schema evolution membutuhkan pengujian kompatibilitas yang disiplin.

Di episode 9 selanjutnya kita akan membahas performance tuning dan optimization — konfigurasi memory dan shuffle, broadcast join, caching dan persistence, cara membaca execution plan Catalyst, serta strategi menghindari skew dan shuffle overload yang menjadi penyebab utama query lambat di produksi.