Episode ini membedah RDD, abstraksi terendah Spark: cara membuat, transformation dan action, serta fungsi fungsional map, filter, flatMap, reduceByKey, dan groupByKey. Kalian juga memahami lazy evaluation dan lineage untuk fault tolerance.

Semua abstraksi tinggi tingkat Spark — DataFrame, Dataset, Spark SQL — pada akhirnya berjalan di atas fondasi bernama RDD. Memahami RDD berarti memahami mesin di bawah kap: bagaimana data didistribusikan, bagaimana transformasi menumpuk, dan bagaimana Spark bertahan dari kegagalan.
Episode 4 ini membahas RDD secara menyeluruh: cara membuatnya, dua jenis operasi (transformation dan action), fungsi-fungsi fungsional paling penting, lazy evaluation, lineage, serta kapan RDD masih menjadi pilihan yang tepat di zaman DataFrame.
RDD (Resilient Distributed Dataset) adalah kumpulan elemen yang immutable, terdistribusi lintas cluster, dan bisa diproses secara paralel. Kata resilient berarti jika sebagian data hilang karena executor gagal, Spark bisa membangun ulang partisi itu dari jejak komputasi (lineage) tanpa harus mengulang seluruh pekerjaan.
RDD menyimpan data mentah apa pun: teks, angka, objek Scala, atau record struktural. Setiap RDD terbagi menjadi partisi yang tersebar ke executor — inilah unit paralelisme yang menentukan berapa banyak task yang dijalankan bersamaan.
Ada beberapa cara membuat RDD. Yang paling umum di eksperimen adalah parallelize, dan di dunia nyata adalah membaca dari sumber penyimpanan:
scala> val rdd = sc.parallelize(Seq("budi", "sari", "dewi"))
scala> rdd.partitions.sizeDi PySpark, ekuivalennya sc.parallelize(["budi", "sari", "dewi"]). Untuk data file, sc.textFile("data/transaksi.csv") membuat satu RDD string per baris. Jumlah partisi bisa diatur lewat argumen kedua parallelize.
Konsep kunci: operasi RDD terbagi dua.
map, filter, flatMap. Belum ada komputasi dijalankan.count(), collect(), reduce().scala> val rdd2 = rdd.map(_.toUpperCase) // transformation, lazy
scala> rdd2.collect() // action, memicu eksekusiSampai ada action, Spark hanya membangun pipeline operasi yang belum dieksekusi. Ini yang memungkinkan Spark mengoptimalkan urutan eksekusi secara keseluruhan.
Tiga transformation paling dasar:
sc = spark.sparkContext
angka = sc.parallelize([1, 2, 3, 4, 5])
hasil_map = angka.map(lambda x: x * 2) # [2, 4, 6, 8, 10]
hasil_filter = angka.filter(lambda x: x % 2 == 0) # [2, 4]
hasil_flatmap = angka.flatMap(lambda x: [x, x + 1]) # [1,2,2,3,3,4,...]map mengubah setiap elemen menjadi tepat satu elemen baru.filter mempertahankan elemen yang memenuhi predikat.flatMap mengubah satu elemen menjadi nol atau lebih elemen, lalu meratakan hasilnya — ideal untuk memecah baris menjadi kata.kalimat = sc.parallelize(["halo dunia", "halo spark"])
kata = kalimat.flatMap(lambda s: s.split(" "))
print(kata.collect())Hasil ['halo', 'dunia', 'halo', 'spark'] menunjukkan cara flatMap memecah dan meratakan — fondasi dari algoritma word count yang legendaris.
Untuk data berpasangan (kunci, nilai), Spark menyediakan agregasi berbasis kunci:
pasangan = sc.parallelize([("apel", 3), ("pisang", 2), ("apel", 1)])
by_key = pasangan.reduceByKey(lambda a, b: a + b) # [('apel',4),('pisang',2)]
grouped = pasangan.groupByKey().mapValues(list) # [('apel',[3,1]),...]Perbedaan penting: reduceByKey menggabungkan nilai sebelum shuffle sehingga data yang dikirim antar node jauh lebih sedikit. groupByKey mengirim semua nilai mentah ke partisi yang sama — sering lebih lambat untuk agregasi sederhana. Aturan praktisnya: pilih reduceByKey bila memungkinkan.
Spark menunda eksekusi sampai ada action. Ini bukan sekadar teknik — ini yang memungkinkan optimasi: Spark bisa menggabungkan transformasi beruntun, memangkas kolom yang tidak dipakai, dan menghindari pekerjaan sia-sia. Jika kalian membangun seratus transformation tanpa action, tidak ada satu pun yang dieksekusi.
Setiap RDD menyimpan jejak transformasi yang membentuknya — inilah lineage. Jika sebuah partisi hilang, Spark menelusuri lineage dan menghitung ulang hanya partisi tersebut. Ini adalah mekanisme fault tolerance yang tidak membutuhkan replikasi data, sebuah keunggulan arsitektural RDD sejak awal.
textFile → flatMap → map → filter → reduceByKeyMenampilkan lineage sebuah RDD bisa dilakukan dengan rdd.toDebugString — sangat berguna saat ingin memahami dari mana sebuah RDD berasal atau mengapa recompute terjadi.
Info
Lineage adalah pedang bermata dua: tanpa cache, setiap action mengulang seluruh rantai komputasi dari sumber. Jika kalian menjalankan banyak action pada RDD yang sama, cache atau persist hasil antara untuk menghindari rekalkulasi berulang.
RDD masih relevan ketika kalian butuh kontrol paling rendah: memanipulasi data yang bukan berbentuk tabel, membangun library transformasi kustom, atau bekerja dengan data yang tidak punya schema tetap. Sebagian besar ekosistem MLlib klasik dan beberapa library pihak ketiga masih mengonsumsi RDD.
Untuk sebagian besar workload, DataFrame adalah pilihan yang lebih baik:
DataFrame pada dasarnya adalah RDD dengan schema dan optimizer — jadi mempelajari RDD di episode ini tetap berharga, karena semua konsep partisi, transformasi, dan action berlaku sama di lapisan atas.
data berkolom & berstruktur → DataFrame / Dataset / SQL
kontrol rendah & data bebas → RDDEpisode 4 menuntaskan pemahaman fondasi: RDD adalah koleksi terdistribusi yang immutable dan resilient, dijalankan lewat transformation lazy dan action eager. Operasi fungsional seperti map, filter, flatMap, reduceByKey, dan groupByKey adalah vokabulari dasar yang muncul kembali di seluruh lapisan Spark.
Inti yang harus dibawa pulang:
flatMap memecah dan meratakan; reduceByKey lebih efisien dari groupByKey.Di episode 5 selanjutnya kita akan membahas DataFrame dan Spark SQL — membuat DataFrame dari CSV, JSON, dan Parquet, menjalankan SQL dengan temporary views, memahami schema inference dan Catalyst optimizer, serta transformation dan expression API yang menjadi bahasa sehari-hari Spark.