Apache Spark adalah mesin analitik terdistribusi yang memproses data dengan membagi pekerjaan ke banyak mesin sekaligus. Topik ini menjelaskan arsitekturnya, yaitu peran driver, cluster manager, dan executor, serta abstraksi data RDD dan DataFrame. Pembahasan dilanjutkan dengan pembagian kerja menjadi job, stage, dan task, kemudian ditutup dengan evaluasi malas yang menjadi ciri model eksekusinya. Pemahaman arsitektur ini menjadi dasar untuk memahami perilaku PySpark ketika dicoba secara langsung.
Arsitektur driver dan executor#
Aplikasi Spark terdiri atas satu proses driver dan banyak proses executor yang berjalan pada node cluster, dengan cluster manager sebagai penyedia sumber dayanya. Diagram berikut memperlihatkan ketiga komponen tersebut beserta alur permintaan sumber daya dan pengiriman task.
Driver adalah proses yang menjalankan fungsi utama aplikasi dan membuat konteks eksekusi Spark. Driver menguraikan kode yang ditulis, membangun rencana eksekusinya, lalu menjadwalkan pekerjaan ke executor. Seluruh keputusan terpusat pada driver, sehingga driver harus berjalan dekat dengan node pekerja dan tetap hidup selama aplikasi berjalan.
Cluster manager adalah layanan eksternal yang mengalokasikan sumber daya cluster untuk aplikasi. Spark tidak terikat pada satu cluster manager tertentu dan mendukung manager bawaan bernama Standalone, Hadoop YARN, serta Kubernetes. Tugasnya berhenti pada penyediaan executor, sedangkan penjadwalan pekerjaan di dalam aplikasi tetap menjadi urusan driver.
Executor adalah proses yang dijalankan pada node pekerja untuk satu aplikasi dan bertugas menjalankan task serta menyimpan data dalam memori atau disk. Setiap aplikasi mendapatkan executor-nya sendiri, dan data tidak dibagikan antar aplikasi tanpa melewati penyimpanan eksternal. Pemisahan ini menjaga isolasi antar aplikasi yang berjalan pada cluster yang sama.
RDD dan DataFrame#
RDD (Resilient Distributed Dataset) adalah abstraksi data paling dasar pada Spark, yaitu koleksi elemen yang terbagi ke banyak partisi dan dapat diolah secara paralel. RDD menyimpan garis keturunan data (lineage), yakni rangkaian operasi yang menghasilkannya, sehingga partisi yang hilang dapat dihitung ulang tanpa menyalin data ke banyak tempat. Partisi inilah yang nantinya menjadi satuan pembagian task, sehingga konsep ini muncul kembali pada pembahasan job dan stage. API RDD memakai objek dan fungsi secara langsung, tanpa informasi tentang kolom dan tipe datanya.
DataFrame adalah lapisan di atas RDD yang menyusun data menjadi tabel dengan kolom bernama dan bertipe, mirip tabel pada basis data relasional. Karena Spark mengetahui skemanya, operasi DataFrame dapat dioptimalkan sebelum dieksekusi, dan itulah alasan API DataFrame menjadi pilihan utama pekerjaan data engineering. PySpark menyediakan API DataFrame tersebut dalam Python, sedangkan eksekusinya tetap berlangsung di JVM.
Job, stage, dan task#
Setiap kali sebuah aksi dipanggil, driver memicu satu job, yaitu satu perhitungan paralel yang tersusun dari banyak task. Job dipecah menjadi stage, yakni kelompok task yang saling bergantung, dan setiap task adalah satu unit kerja yang dikirim ke satu executor untuk satu partisi data. Satu executor menjalankan banyak task dalam beberapa thread secara serentak.
Batas antar stage umumnya muncul ketika data harus ditata ulang antar partisi, misalnya setelah groupBy yang memindahkan baris dengan kunci yang sama ke executor yang sama. Perpindahan semacam ini disebut shuffle, dan perpindahan inilah yang biasanya paling menentukan kecepatan eksekusi terdistribusi karena data bergerak melalui jaringan. Driver mencatat seluruh proses tersebut, dan setiap driver membuka web UI pada port 4040 yang menampilkan job, stage, serta executor yang sedang berjalan.
Struktur job dan stage juga membantu ketika membaca log Spark. Nama keduanya muncul setiap kali pekerjaan berjalan atau gagal, sehingga laporan error yang menyebut stage memberi petunjuk tentang tahap mana yang menjadi sumber masalahnya.
Evaluasi malas#
Spark tidak menjalankan transformasi begitu kode dieksekusi, melainkan menumpuknya menjadi rencana dan baru mengeksekusinya ketika hasil benar-benar diminta. Pola ini disebut evaluasi malas (lazy evaluation). Operasi yang membangun rencana disebut transformasi (transformation), contohnya filter dan groupBy, sedangkan operasi yang memicu eksekusi disebut aksi (action), contohnya show, count, dan collect.
# tidak-dijalankan
# dua baris pertama hanya membangun rencana, belum ada pekerjaan yang berjalan
df = spark.read.parquet("peminjaman.parquet")
panjang = df.filter(df.durasi_hari > 7)
# show() adalah aksi, yaitu pemicu seluruh rencana di atas dieksekusi sekaligus
panjang.show()Pada contoh di atas, pembacaan berkas dan filter hanya menghasilkan objek rencana, dan tidak ada byte data yang dibaca sampai show() dipanggil. Evaluasi malas memberi Spark kesempatan melihat keseluruhan rencana terlebih dahulu, lalu menyusun ulang langkahnya demi mengurangi data yang diproses, misalnya dengan mendorong filter ke tingkat pembacaan berkas. Konsekuensinya, error pada kode sering baru muncul ketika aksi dipanggil, jauh dari baris yang menjadi penyebabnya, sehingga pelacakan error Spark dimulai dari aksi yang memicunya. Rencana yang sudah terbentuk dapat diperiksa sebelum aksi dipanggil melalui method explain() pada DataFrame, sehingga pola evaluasi malas sekaligus menjadi jendela untuk melihat cara Spark menata pekerjaannya.
Kesalahan umum#
Kesalahan berikut bersumber dari kebiasaan membaca Spark seolah-olah kode Python biasa yang berjalan baris demi baris, padahal model eksekusinya berbeda, dan setiap butir menyertakan perbaikannya.
- Menganggap setiap baris kode dieksekusi seketika. Baris transformasi hanya menambah rencana, sehingga hasil yang diperiksa belum tentu berasal dari eksekusi nyata. Perbaikannya adalah menguji rencana melalui aksi kecil pada data sampel sebelum keseluruhan pipeline dijalankan.
- Menarik seluruh data ke driver dengan
collect(). Driver hanya memiliki memori satu mesin, sehinggacollect()pada data besar menghabiskan memorinya. Perbaikannya adalah mengagregasi data di executor terlebih dahulu, lalu hanya menarik hasil akhirnya yang berukuran kecil. - Menyebut setiap pemanggilan fungsi sebagai job. Job hanya muncul pada aksi, sedangkan transformasi hanya membangun rencana. Perbaikannya adalah membaca web UI atau log driver untuk melihat job yang benar-benar terbentuk.
Ringkasan#
- Aplikasi Spark terdiri atas driver yang membangun rencana dan menjadwalkan pekerjaan, cluster manager yang menyediakan executor, serta executor yang menjalankan task dan menyimpan datanya.
- DataFrame adalah API utama yang menyusun data menjadi tabel bertipe, di atas abstraksi RDD yang membagi data menjadi partisi paralel.
- Aksi memicu job yang terbagi menjadi stage dan task, dengan shuffle antar stage sebagai bagian eksekusi yang paling sering menentukan kecepatannya.
- Evaluasi malas menunda eksekusi sampai aksi dipanggil, sehingga Spark dapat mengoptimalkan seluruh rencana terlebih dahulu.
Referensi#
- Cluster Mode Overview — Apache Spark — Dokumentasi resmi komponen aplikasi Spark, yaitu driver, cluster manager, dan executor, beserta istilah task, job, dan stage.
- RDD Programming Guide — Apache Spark — Panduan resmi RDD, termasuk partisi, lineage, serta operasi transformasi dan aksi.
- pyspark.sql.DataFrame.explain — Apache Spark
— Referensi API method
explain()untuk memeriksa rencana eksekusi tanpa menjalankannya.