Kursus
Pernah mencoba men-debug job Spark yang tiba-tiba gagal dan merasa benar-benar tersesat karena betapa dalamnya “lubang kelinci” Spark?
Saat pertama kali bekerja dengan Apache Spark, saya pikir saya hanya perlu menulis beberapa transformasi PySpark dan Spark akan “secara ajaib” melakukan skala ke seluruh klaster. Saya keliru. Kinerja Spark sepenuhnya bergantung pada pemahaman apa yang terjadi di balik layar.
Panduan ini untuk siapa pun yang tidak ingin memperlakukan Spark seperti kotak hitam. Kita akan menelusuri bagaimana arsitektur Spark dirancang, mulai dari model master–worker dan alur eksekusi, hingga manajemen memori serta mekanisme toleransi kesalahannya.
Jika Anda ingin membangun aplikasi big data yang cepat, tangguh terhadap kegagalan, dan efisien, Anda berada di tempat yang tepat!
Arsitektur Dasar Apache Spark
Sebelum Anda menulis baris pertama PySpark, Spark sudah membuat beberapa keputusan arsitektural untuk Anda. Spark tidak hanya cepat karena komputasi in-memory, tetapi juga karena dibangun di atas arsitektur master–worker yang dapat melakukan skala dan bertahan menghadapi kekacauan dunia nyata seperti kerusakan node, crashes, Java Virtual Machine (JVM) issues, dan volume data yang tidak konsisten.
Mari kita uraikan arsitektur inti Spark dan mengapa ia masih begitu kuat dan hadir dalam alur kerja big data modern.
Paradigma master–worker
Inti Spark adalah model master–worker. Bayangkan seperti ini:
- Driver (master): Ini adalah “otak” Spark. Ia menjalankan fungsi
main()Anda, membuat Spark context, menangani penjadwalan DAG, dan memberi tahu klaster apa yang harus dilakukan. - Executor (worker): Ini adalah “ototnya”. Mereka mengeksekusi tugas Anda, menyimpan data di memori, dan melapor kembali ke driver.
Susunan ini memungkinkan Anda fokus mendefinisikan transformasi, dan Spark yang memutuskan di mana serta bagaimana menjalankannya secara paralel pada executor.
Yang saya sukai dari desain ini adalah sifatnya yang agnostik terhadap deployment. Kode yang sama berjalan apa pun target deployment-nya—di mesin lokal Anda, di Kubernetes, atau Mesos. Ini memudahkan pengembangan dan pengujian lokal, lalu melakukan skala ke klaster tanpa menulis ulang kode.
Dan inilah manfaat kuat lain dari pemisahan driver–worker Spark: Ia meningkatkan isolasi kesalahan. Jika sebuah node worker mati saat mengeksekusi tugas, Spark dapat menetapkan ulang tugas itu ke worker lain tanpa membuat aplikasi Anda crash.
Komponen inti
Mari kita uraikan apa yang terjadi di dalam driver dan node.

Arsitektur Spark. Gambar oleh penulis.
Spark context
Saat Anda memanggil SparkContext() atau menggunakan SparkSession.builder.getOrCreate(), Anda membuka gerbang ke seluruh “sihir” internal Spark.
Spark context:
- Tersambung ke pengelola klaster Anda
- Mengalokasikan executor
- Melacak status job dan rencana eksekusi
Spark membangun Directed Acyclic Graph (DAG) transformasi di balik layar. DAG itu dipecah menjadi stage dan task, lalu dieksekusi secara paralel.
DAG scheduler menentukan tugas mana yang bisa dijalankan bersamaan, dan Task scheduler menugaskannya ke executor. Sementara itu, Block manager memastikan data di-cache, di-shuffle, atau dimuat ulang sesuai kebutuhan.
Desain berlapis ini membuat Spark sangat fleksibel, karena Anda bisa menyetel memori, penyimpanan, dan komputasi secara independen.
Jika Anda bekerja dengan transformasi Spark atau rekayasa fitur, lihat kursus Feature Engineering with PySpark untuk melihat arsitektur ini beraksi.
Runtime executor
Executor adalah tempat pekerjaan dilakukan.
Setiap executor menjalankan:
- Satu atau lebih task (berbasis thread)
- Sejumlah memori untuk caching data dan output shuffle
- Instans JVM sendiri, terisolasi dari yang lain
Anda dapat mengonfigurasi berapa banyak memori yang didapat tiap executor, berapa banyak core yang digunakan, dan apakah harus menulis ke disk saat memori habis.
Namun, hati-hati: Jika Anda tidak mengalokasikan memori yang cukup, Anda akan sering terkena error kehabisan memori. Di sisi lain, mengalokasikan terlalu banyak memori juga membuang sumber daya. Pemantauan dan penyetelan sangat penting di sini.
Alur Eksekusi: Dari Kode ke Klaster
Menulis kode PySpark terasa cukup sederhana. Anda memfilter DataFrame, melakukan join, mengagregasi sesuatu, lalu menjalankannya. Namun di balik API yang rapi itu, Spark diam-diam memutar mesin eksekusi yang dapat menyebarkan pekerjaan ke beberapa node.
Mari kita telusuri apa yang terjadi di balik layar.
Konversi rencana logis ke fisik
Inilah yang kebanyakan pengguna Spark tidak sadari pada awalnya: Saat Anda menulis kode PySpark, Anda tidak langsung menjalankan apa pun. Anda sedang membangun sebuah rencana, dan Catalyst Optimizer milik Spark mengambil rencana itu dan mengubahnya menjadi strategi eksekusi yang efisien.
Prosesnya berlangsung dalam empat tahap:
- Analisis: Spark menyelesaikan nama kolom, tipe data, dan referensi tabel, memastikan semuanya valid.
- Optimasi logis: Di sinilah Spark menerapkan aturan seperti predicate pushdown dan constant folding. Ia mengoptimalkan filter dan menggabungkan proyeksi.
- Perencanaan fisik: Spark mempertimbangkan beberapa strategi eksekusi dan memilih yang paling efisien (berdasarkan ukuran data, partisi, dll.).
- Generasi kode: Terakhir, Spark menggunakan whole-stage code generation untuk menghasilkan bytecode JVM.

Catalyst Optimizer Spark. Gambar oleh Databricks.
Jadi rangkaian .select(), .join(), dan .groupBy() itu tidak hanya berjalan baris demi baris. Ia dianalisis, dioptimalkan, dan dikompilasi menjadi sesuatu yang berjalan cepat di klaster.
Lihat PySpark Cheat Sheet ini jika Anda menginginkan ringkasan perintah PySpark paling berguna.
DAG scheduler & pembuatan stage
Saat rencana selesai, DAG scheduler mengambil alih.
Ia memecah job menjadi beberapa stage berdasarkan batas shuffle, di mana Spark memutuskan apa yang terjadi secara berurutan dan apa yang bisa dieksekusi paralel.
Ada dua tipe stage utama:
- ShuffleMapStage: Melibatkan shuffle, yang biasanya disebabkan oleh transformasi lebar seperti
groupBy()ataujoin(). Data kemudian dipartisi dan dikirim melalui jaringan. Tipe stage ini diperlukan untuk menghitung ResultStage. - ResultStage: Stage yang menghasilkan output, seperti menulis ke disk atau mengembalikan hasil ke driver.
Satu hal penting yang saya pelajari adalah meminimalkan shuffle. Shuffle harus terjadi sebelum sebuah stage selesai dan mahal biayanya. Anda perlu memahami di mana shuffle terjadi dalam DAG Anda dan apakah Anda bisa mengoptimalkan kode lebih lanjut untuk mengurangi jumlah shuffle.
Siklus hidup eksekusi task
Setelah DAG scheduler membuat semua stage, tahap-tahap tersebut dapat dieksekusi pada berbagai executor.
Siklus hidup eksekusi task kira-kira seperti ini:
- Serialisasi task: Driver men-serialisasi instruksi task dan mengirimkannya ke executor.
- Fase penulisan shuffle: Spark menulis output yang dipartisi ke disk lokal.
- Fase fetch: Executor pada stage berikutnya mengambil berkas shuffle terkait dari executor lain di seluruh klaster.
- Deserialisasi dan eksekusi: Executor mendeserialisasi data, menjalankan logika Anda, dan berpotensi melakukan cache atau menulis hasil.
- Garbage collection: JVM secara otomatis mengambil kembali memori yang tidak lagi digunakan aplikasi Spark. Langkah ini penting untuk mencegah kebocoran memori dan memastikan aplikasi Spark berjalan mulus.
Sedikit petunjuk dari pengalaman saya sendiri: jika job Spark Anda menggantung setelah sebelumnya berjalan baik, sering kali penyebabnya adalah garbage collection atau keterlambatan fetch shuffle. Selalu periksa kode Anda dan pastikan Anda memahami arsitektur Spark agar dapat mengoptimalkan topik-topik ini secara efektif.
Arsitektur Manajemen Memori
Manajemen memori Spark adalah topik yang sangat kompleks dan dapat menghabiskan berjam-jam debugging jika Anda tidak memahaminya.
Mari kita lihat bagaimana Spark mengelola memori di balik layar agar Anda menyadarinya dan dapat menghindari berjam-jam debugging kode lambat atau error kehabisan memori.
Model memori terpadu
Sebelum Spark 1.6, memori dibagi ketat antara eksekusi (untuk shuffle dan join) dan penyimpanan (untuk caching). Itu berubah dengan Spark 1.6, yang memperkenalkan model memori terpadu.
Dalam model memori terpadu, data dibagi menjadi tiga pool utama:
- Reserved memory: Sejumlah kecil memori digunakan untuk internal Spark dan sistem.
- Spark memory: Digunakan untuk menyimpan data eksekusi dan untuk caching. Ini dibagi secara dinamis. Jika job Anda butuh lebih banyak memori untuk shuffle dan lebih sedikit untuk caching (atau sebaliknya), Spark menyesuaikan.
- User memory: Ruang untuk struktur data yang ditentukan pengguna yang diperlukan untuk menjalankan kode pengguna di dalam aplikasi Spark.
Pool memori Spark selanjutnya dibagi menjadi dua pool:
- Executor memory: Menyimpan data sementara yang diperlukan selama tahap pemrosesan task (mis. shuffle, join, agregasi, …).
- Storage memory pool: Digunakan untuk caching data dan menyimpan struktur data internal.
Elastisitas ini memungkinkan Spark menjadi lebih fleksibel menghadapi volume data yang tidak dapat diprediksi.
Namun, ini juga berarti sedikit kehilangan kontrol ketika Anda tidak tahu apa yang terjadi. Misalnya, jika Anda melakukan cache() pada DataFrame besar tetapi juga memiliki agregasi mahal dalam stage yang sama, Spark mungkin mengusir data yang di-cache untuk memberi ruang bagi shuffle.
Off-heap & penyimpanan kolumnar
Dalam penyimpanan off-heap dan kolumnar Spark, mesin Tungsten berperan.
Tungsten memperkenalkan beberapa optimasi yang meningkatkan kinerja Spark:
- Manajemen memori off-heap: Spark kini menyimpan sebagian data di luar heap JVM, mengurangi overhead garbage collection dan membuat manajemen memori lebih prediktif.
- Penyimpanan format biner: Data disimpan dalam bentuk biner yang ringkas dan ramah-cache, yang meningkatkan penggunaan CPU dan memungkinkan eksekusi ter-vektor.
- Algoritme sadar-cache: Spark kini dapat menggunakan cache CPU lebih efektif, menghindari pembacaan yang tidak perlu dari RAM atau disk.
Dan jika Anda bekerja dengan DataFrame, Anda sudah menggunakan optimasi ini di balik layar. Itulah salah satu alasan saya mendorong orang menggunakan DataFrame dan API SQL alih-alih RDD mentah. Anda mendapatkan seluruh kekuatan Catalyst dan Tungsten tanpa penyetelan tambahan.
Jika Anda mengerjakan pipeline pembersihan data, Anda akan melihat ini beraksi di Cleaning Data with PySpark.
Mekanisme Toleransi Kesalahan
Jika Anda bekerja dengan sistem terdistribusi, ada satu hal yang pasti: sistem itu bisa gagal. Node crash. Error jaringan terjadi. Executor kehabisan memori dan mati.
Namun Spark dibangun untuk menangani masalah-masalah ini dan memastikan job Anda tetap berhasil.
Mari kita telusuri lebih dalam bagaimana Spark memastikan job Anda tetap berhasil, bahkan jika terjadi ketidakstabilan.
Pelacakan lineage RDD
Resilient Distributed Datasets (RDD) adalah struktur data fundamental di Spark. Dan mereka disebut “resilient” bukan tanpa alasan.
Spark menggunakan lineage untuk memastikan setiap RDD dapat dihitung ulang jika terjadi kegagalan node dan kehilangan data.
Jadi ketika sebuah node gagal, Spark cukup menghitung ulang data yang hilang menggunakan graf lineage.
Begini cara kerjanya dalam praktik:
- Dependensi sempit (seperti
map()ataufilter()): Spark hanya perlu partisi yang hilang untuk menghitung ulang. - Dependensi lebar (seperti
groupBy()ataujoin()): Spark mungkin perlu mengambil data dari beberapa partisi, karena bisa memerlukan output dari beberapa stage.
Lineage menghindarkan Anda dari perlu menangani kegagalan secara manual. Namun, jika graf lineage Anda menjadi terlalu panjang—misalnya mengandung ratusan transformasi—menghitung ulang data yang hilang menjadi mahal. Di sinilah checkpointing berperan.
Checkpointing & write-ahead logs
Saat Anda menghadapi alur kerja kompleks atau job streaming, Spark tidak bisa hanya bergantung pada lineage. Di situlah checkpointing berperan.
Anda dapat memanggil rdd.checkpoint() untuk menyimpan status RDD saat ini ke lokasi penyimpanan andal (seperti HDFS).
Spark lalu memangkas lineage. Jika terjadi error, Spark memuat ulang data secara langsung alih-alih menghitung ulang.
Dalam structured streaming, Spark juga menggunakan write-ahead log (WAL) untuk memastikan data tidak hilang saat transit.
Inilah yang membuatnya stabil:
- Reliable receiver: Mereka menulis data masuk ke log sebelum diproses.
- Heartbeat executor: Sinyal rutin ini mengonfirmasi executor tetap hidup dan sehat.
- Direktori checkpoint: Untuk job streaming, menyimpan offset, metadata, dan state output agar Anda dapat melanjutkan dari titik terakhir.
Checkpointing bersifat opsional untuk job batch, tetapi wajib untuk pipeline streaming.
Bayangkan Anda memiliki job Spark yang gagal setelah berjalan 10 jam, tetapi Anda dapat melanjutkan dari titik terakhir berkat checkpointing dan WAL.
Fitur Arsitektural Lanjutan
Sampai di sini, Anda telah melihat bagaimana Spark memproses job serta menangani memori dan kegagalan.
Di bagian ini, kita menyelami beberapa peningkatan arsitektural tingkat lanjut yang membuat Spark lebih dinamis, lebih real-time, dan lebih adaptif.
Adaptive query execution (AQE)
AQE diperkenalkan di Spark 3.0 dan meningkatkan kinerja kueri dengan menyesuaikan rencana eksekusi secara dinamis saat runtime berdasarkan statistik yang dikumpulkan selama eksekusi.
Fitur AQE meliputi:
- Beralih strategi join secara dinamis: Jika broadcast join Anda tidak muat di memori, AQE beralih ke sort-merge join.
- Menggabungkan partisi shuffle: Menggabungkan partisi shuffle kecil menjadi lebih besar, sehingga mengurangi overhead.
- Menangani data skewed: AQE dapat membagi partisi yang skewed untuk menyeimbangkan waktu eksekusi.
Fitur ini mengubah permainan, karena memungkinkan job yang sebelumnya memerlukan penyetelan manual dan coba-coba untuk beradaptasi secara real-time.
Pastikan untuk mengaktifkannya secara eksplisit melalui konfigurasi (spark.sql.adaptive.enabled = true). Dan jika Anda menjalankan Spark 3.0+, tidak ada alasan untuk tidak mengaktifkannya.
Arsitektur structured streaming
Structured Streaming membawa mesin Spark ke ranah real-time, tanpa mengharuskan Anda mempelajari API yang benar-benar baru.
Di balik layar, Spark tetap menerapkan micro-batching. Namun Spark menangani:
- Manajemen offset: Spark melacak dengan tepat data mana yang telah dibaca dari sumber Anda (Kafka, socket, file, dll.). Ini memberikan jaminan exactly-once yang kuat saat dikonfigurasi dengan benar.
- Watermarking: Dengan agregasi berbasis waktu, Spark menggunakan watermark untuk memutuskan kapan data terlambat dianggap terlalu terlambat untuk disertakan. Ini krusial untuk pemrosesan event-time.
- State store: Saat Anda melakukan agregasi berjendela atau join streaming, Spark mempertahankan state antar micro-batch. State ini disimpan di disk dan di-checkpoint untuk menghindari kehilangan data.
Yang kuat di sini adalah bagaimana streaming terasa seperti batch. Anda menulis groupBy() atau filter() dan Spark menangani sisanya, membuat analitik streaming dapat diakses tanpa rantai alat khusus.
Arsitektur Keamanan
Jika Anda menjalankan Spark di produksi, terutama di keuangan, layanan kesehatan, atau area bisnis serupa, Anda perlu tahu bagaimana Spark menangani autentikasi, enkripsi, dan auditabilitas.
Mari kita selami topik-topik ini dan bagaimana Spark menanganinya.
Autentikasi & enkripsi
Spark memiliki banyak fitur keamanan yang harus Anda aktifkan terlebih dahulu. Namun setelah diaktifkan, Spark menawarkan perangkat yang solid untuk komunikasi aman dan autentikasi:
- Autentikasi (SASL): Spark menggunakan Simple Authentication and Security Layer (SASL) untuk memverifikasi bahwa hanya pengguna dan layanan berwenang yang dapat mengirimkan job atau terhubung ke klaster.
- Enkripsi in-transit (AES-GCM, SSL/TLS): Spark mengenkripsi komunikasi antar-node menggunakan AES-GCM (authenticated encryption) atau TLS. Ini melindungi data job dari penyadapan, sangat penting di lingkungan multi-tenant atau cloud.
- Integrasi Kerberos: Jika Anda menjalankan di Hadoop/YARN, Spark terintegrasi dengan Kerberos untuk autentikasi pengguna yang aman. Ini mengaitkan job Spark Anda langsung ke sistem manajemen identitas dan akses perusahaan.
- Kontrol akses UI: Spark Web UI dapat membocorkan info sensitif (seperti log, path input, kueri SQL), jadi atur
spark.acls.enable=truesertaspark.ui.view.aclsdanspark.ui.view.acls.groupsuntuk membatasinya.
Anda dapat memeriksa semua fitur keamanan di dokumentasi resmi Spark. Tinjau dan pastikan Anda mengaktifkan fitur yang diperlukan untuk mengamankan aplikasi Spark Anda.
Audit & kepatuhan
Mencatat siapa melakukan apa dan kapan juga sangat penting.
Spark mendukung:
- Event logging: Saat diaktifkan (
spark.eventLog.enabled=true), Spark mencatat setiap event job, stage, dan task ke disk. Anda dapat menggunakan log ini untuk memutar ulang riwayat job atau memenuhi persyaratan audit. - Role-based access control (RBAC): Spark tidak menyediakan RBAC, tetapi jika Anda menggunakan Spark melalui platform seperti Databricks, EMR, atau OpenShift, biasanya Anda mendapatkan RBAC di lapisan infrastruktur. Spark mengirimkan job menggunakan identitas yang ditentukan, yang mengontrol akses ke data dan sumber daya komputasi.
- Masking data dan kontrol akses di sumber: Spark membaca dari banyak sumber (Parquet, Delta Lake, Hive, dll.), dan kontrol akses Anda sebaiknya ditegakkan di sana.
Pola Optimasi Kinerja
Spark sangat kuat dan cepat, dan dapat dioptimalkan agar lebih cepat lagi jika Anda tahu di mana harus melakukan penyesuaian.
Ada beberapa area tempat Anda bisa mencoba mengoptimalkan untuk memaksimalkan Spark. Mari kita dalami tiap area.
Optimasi shuffle
Jika Spark memiliki titik lemah, itu adalah shuffle. Shuffle terjadi saat data perlu dipindahkan antar partisi, biasanya setelah transformasi lebar seperti groupByKey(), distinct(), atau join().
Dan ketika shuffle bermasalah, Anda bisa mengalami I/O disk yang sangat besar, jeda garbage collection yang panjang, atau task skewed yang tak kunjung selesai.
Berikut cara meningkatkan shuffle:
- Lebih suka
reduceByKey()daripadagroupByKey():reduceByKey()mengagregasi secara lokal sebelum shuffle.groupByKey()mengirim semuanya melalui jaringan. - Repartition secara cermat: Gunakan
.repartition(n)untuk meningkatkan paralelisme, atau.coalesce(n)untuk menguranginya. Jangan biarkan Spark memakai partisi default begitu saja. - Gunakan broadcast join (secara bijak): Jika salah satu dataset cukup kecil, siarkan ke semua worker. Atur
spark.sql.autoBroadcastJoinThresholduntuk mengendalikan batas ukurannya. - Hindari
collect(): Hindari jika memungkinkan, karena menarik data ke driver akan menghancurkan kinerja.
Panduan konfigurasi memori
Menyetel memori Spark bisa terasa seperti sains tersendiri, tetapi Anda bisa menggunakan daftar periksa di bawah ini untuk memudahkan:
- Alokasikan memori yang cukup: Mulailah dengan setidaknya 6 GB memori untuk klaster Spark dan sesuaikan berdasarkan kebutuhan spesifik Anda.
- Pertimbangkan fraksi memori Spark: Secara default, 60% adalah fraksi memori di Spark. Naikkan jika aplikasi Anda sangat bergantung pada operasi DataFrame/Dataset atau jika Anda membutuhkan lebih banyak user memory.
- Gunakan jumlah core per executor yang tepat: Biasanya 3–5 itu optimal. Terlalu sedikit menyebabkan underutilization, terlalu banyak menyebabkan kontensi task.
- Aktifkan alokasi dinamis (jika didukung): Spark dapat menaikkan/menurunkan jumlah executor berdasarkan beban kerja.
spark.dynamicAllocation.enabled=true
spark.dynamicAllocation.minExecutors=2
spark.dynamicAllocation.maxExecutors=20
- Sesuaikan storage fraction: Jika Anda butuh lebih banyak caching, tingkatkan nilai
spark.memory.storageFraction. - Pantau dan profil penggunaan memori: Manfaatkan alat seperti Spark UI atau VisualVM untuk melacak konsumsi memori dan menemukan bottleneck.
Menyesuaikan konfigurasi memori bisa sangat membantu. Saya pernah memangkas job 30 menit menjadi 8 menit hanya dengan menyesuaikan konfigurasi memori, tanpa mengubah satu baris kode pun.
Rumus penentuan ukuran klaster
Ini adalah bagian yang paling sering keliru, karena banyak tim menebak ukuran klaster alih-alih memperkirakannya dengan benar.
Namun Anda bisa berbuat lebih baik dengan menggunakan rumus di bawah ini:
- Tentukan jumlah partisi:
- Hitung jumlah partisi yang dibutuhkan berdasarkan ukuran data dan ukuran partisi yang diinginkan.
- Pedoman standar adalah satu partisi per 128 MB hingga 256 MB data tidak terkompresi.
- Rumus: Jumlah Partisi = Pembulatan ke atas(Total Ukuran Data ÷ Ukuran Partisi).
- Hitung total jumlah core:
- Jumlah core harus cukup untuk memproses semua partisi secara paralel.
- Rumus: Total Core = Pembulatan ke atas(Jumlah Partisi ÷ Partisi per Core).
- Tentukan memori per executor:
- Hitung jumlah memori yang dibutuhkan tiap executor berdasarkan core, ukuran partisi, dan overhead.
- Rumus: Memori per Executor = Memori Dasar × (1 + Persentase Overhead).
- Hitung jumlah executor:
- Tentukan jumlah executor berdasarkan total jumlah core dan core per executor.
- Rumus: Jumlah Executor = Pembulatan ke atas(Total Core ÷ Core per Executor).
- Hitung total memori:
- Hitung total memori yang dibutuhkan klaster berdasarkan jumlah executor dan memori per executor.
- Rumus: Total Memori = Jumlah Executor × Memori per Executor.
Contoh:
- Input: 500GB data dan ukuran partisi ~128MB
- Partisi: ~4.000 partisi
- Core: 4.000 partisi / 4 partisi per core = 1.000
- Memori per executor: Asumsikan 8 GB per executor dan 20% overhead. 8 GB * 1,20 = 9,6 GB
- Executor: 1.000 core / 4 core per executor = 250 executor
- Total memori: 250 executor * 9,6GB = 2.400 GB
Namun ingat: Ini hanya perkiraan. Gunakan sebagai titik awal lalu optimalkan lebih lanjut melalui profiling.
Tren Arsitektural yang Muncul
Spark telah ada selama satu dekade, tetapi tetap sangat relevan. Spark berkembang lebih cepat dari sebelumnya, berkat platform cloud-native, akselerasi GPU, dan integrasi ML yang lebih erat.
Jika Anda menggunakan Spark hari ini dengan cara yang sama seperti tiga tahun lalu, kemungkinan Anda menyisakan performa dan melewatkan fitur-fitur baru yang hebat.
Mari kita lihat beberapa yang terbaru.
Mesin Photon (Databricks)
Jika Anda bekerja dengan Databricks, Anda mungkin sudah pernah bekerja dengan dan mendengar tentang Photon.
Jika Anda ingin mempelajari lebih lanjut tentang Databricks, saya merekomendasikan kursus Introduction to Databricks.
Photon adalah mesin generasi berikutnya di platform Databricks Lakehouse yang memberikan kinerja kueri cepat dengan biaya rendah. Photon kompatibel dengan API Spark, sehingga Anda tidak perlu menyesuaikan kode Spark untuk memanfaatkannya.
Photon membantu meningkatkan secara signifikan kode SQL dan PySpark Anda.
Photon mencakup fitur-fitur berikut:
- Eksekusi ter-vektor: Photon memproses data dalam batch kolumnar, memanfaatkan instruksi CPU SIMD (Single Instruction, Multiple Data) untuk melakukan operasi pada banyak nilai sekaligus. Spark tradisional menggunakan eksekusi baris-demi-baris dan sangat bergantung pada JVM untuk alokasi memori dan garbage collection.
- Runtime C++ (tanpa overhead JVM): Tidak ada garbage collection Java, yang bisa menjadi bottleneck pada job Spark besar. Memori dikelola secara presisi di C++.
- Peningkatan optimasi kueri: Photon terintegrasi mendalam dengan Catalyst Optimizer Spark, namun juga menyertakan optimasi saat eksekusi (seperti penyaringan runtime, jalur kode adaptif, optimasi join dan agregasi).
- Akselerasi perangkat keras: Dukungan untuk perangkat keras modern (seperti GPU NVIDIA, set instruksi AVX-512 untuk CPU Intel, prosesor Graviton (ARM) di AWS).
Serverless Spark
Serverless itu fantastis, karena berarti Anda tidak perlu mengelola klaster, melakukan pra-penyediaan sumber daya, dan Anda hanya membayar saat Spark berjalan.
Dan serverless untuk Spark sudah tersedia di layanan seperti Databricks Serverless, AWS Glue, dan GCP Dataproc Serverless.
Inilah alasannya begitu luar biasa:
- Skala otomatis: Platform menskalakan komputasi berdasarkan kebutuhan nyata job Anda, artinya Anda tidak perlu menebak berapa banyak node yang dibutuhkan.
- Hemat biaya: Anda hanya membayar sesuai yang digunakan. Tidak ada lagi membayar server menganggur.
- Kesederhanaan: Tidak perlu repot dengan penyiapan, konfigurasi, atau pemeliharaan klaster, karena itu ditangani untuk Anda.
- Kinerja: Waktu eksekusi bisa lebih cepat, karena konfigurasi dan penyiapan telah dioptimalkan untuk Anda.
Serverless Spark ideal untuk analitik interaktif, job ad-hoc, atau beban kerja yang tidak dapat diprediksi.
Namun hati-hati: pipeline jangka panjang yang konsisten mungkin tetap lebih murah di klaster tetap. Selalu ukur baik biaya maupun latensi.
Integrasi MLflow
Saat industri berubah, batas antara rekayasa data dan AI semakin kabur. Seperti yang dibahas Deepak Goyal, CEO & Founder di Azurelib Academy, di podcast DataFramed
Rekayasa data akan memainkan peran penting dan fundamental dalam pergeseran menuju AI yang akan datang.
Deepak Goyal, CEO & Founder at Azurelib Academy
Jika Anda melakukan pembelajaran mesin dalam skala besar dan menargetkan penerapan model ke produksi, Spark saja tidak cukup. Anda memerlukan prinsip MLOps, seperti pelacakan eksperimen, versi model, dan reproduktabilitas. Di situlah MLflow berperan.
MLflow kini terintegrasi dengan Spark dan membawa tumpukan MLOps lengkap ke pipeline Anda.
Anda dapat:
- Lacak eksperimen: Catat parameter, metrik, dan artefak dari job Spark ML menggunakan
mlflow.log_param()danmlflow.log_metric(). - Versikan model: Simpan model dari
pyspark.mlatausklearnlangsung ke registry model MLflow. - Melayani model: Deploy model terlatih ke endpoint REST menggunakan model serving MLflow.
Anda tidak perlu berganti alat. Anda tetap menggunakan Spark untuk pelatihan, rekayasa fitur, dan penilaian, sambil memanfaatkan MLflow untuk tugas MLOps.
Kesimpulan
Jika Anda belum banyak tahu tentang Spark, ia seperti kotak hitam raksasa. Anda menulis sedikit kode PySpark, menekan run, dan berharap semuanya berjalan.
Kadang itu berhasil untuk saya, kadang berujung pada sesi debugging panjang dan mencari apa yang salah.
Baru ketika saya mulai melihat ke balik layar, semuanya masuk akal bagi saya. Dan butuh waktu cukup lama bagi saya untuk memahami apa yang sebenarnya terjadi.
Inilah yang akan saya fokuskan jika saya mulai dari nol lagi:
- Pelajari bagaimana Spark memecah kode Anda menjadi job, stage, dan task.
- Pahami memori.
- Waspadai shuffle.
- Mulai dari yang kecil dan jalankan dalam mode lokal. Terjun langsung.
Itulah tepatnya yang kita pelajari dalam artikel ini.
Jika Anda ingin terus belajar, berikut beberapa sumber ramah pemula yang saya rekomendasikan:
- Introduction to PySpark: Titik awal praktis yang bagus jika Anda masih beradaptasi.
- Cleaning Data with PySpark: Pelajari cara membersihkan data, karena data dunia nyata selalu berantakan.
- The Top 20 Spark Interview Questions: Bukan hanya untuk wawancara, tetapi untuk memperdalam pemahaman Anda.
- Top 4 Apache Spark Certifications in 2025: Jika Anda ingin mengakui keahlian melalui sertifikasi.
FAQs
Bagaimana cara memilih cluster manager yang tepat untuk deployment Spark saya?
Spark mendukung beberapa pengelola klaster (YARN, Mesos, Kubernetes, dan standalone). Pilihan Anda bergantung pada infrastruktur yang ada, kebutuhan berbagi sumber daya, dan keahlian operasional: YARN terintegrasi baik pada klaster Hadoop, Kubernetes menawarkan portabilitas berbasis kontainer, dan Mesos unggul dalam isolasi multi-tenant.
Apa itu external shuffle service dan bagaimana cara meningkatkan kinerja?
External shuffle service memisahkan penyajian berkas shuffle dari siklus hidup executor, memungkinkan alokasi dinamis dan mengurangi kehilangan data saat executor dihentikan. Layanan ini menjaga berkas shuffle tetap tersedia bahkan setelah executor mati, yang mempercepat percobaan ulang stage dan menghemat I/O disk di bawah beban berat.
Bagaimana broadcast join bekerja secara internal dan kapan saya harus menggunakannya?
Untuk broadcast join, Spark mengirim tabel lookup kecil ke setiap executor untuk menghindari shuffle data penuh. Gunakan saat satu sisi join berada di bawah spark.sql.autoBroadcastJoinThreshold (default 10 MB), karena ini sangat mengurangi I/O jaringan dan mempercepat join pada distribusi kunci yang skewed.
Apa praktik terbaik untuk menyetel garbage collection JVM di Spark?
Pantau jeda GC melalui Spark UI atau alat seperti VisualVM dan pilih kolektor G1GC untuk waktu jeda yang rendah. Alokasikan memori executor dengan ruang ekstra untuk overhead (spark.executor.memoryOverhead) dan setel -XX:InitiatingHeapOccupancyPercent untuk memicu GC lebih awal, mencegah jeda stop-the-world yang panjang.
Bagaimana saya dapat memanfaatkan akselerasi GPU untuk mempercepat job Spark?
Gunakan NVIDIA RAPIDS Accelerator for Apache Spark untuk secara transparan mengalihkan operasi SQL dan DataFrame ke GPU. Akselerator ini terpasang ke mesin eksekusi Spark, mengganti operator berbasis CPU dengan padanan yang dipercepat GPU dan menawarkan pemrosesan hingga 10× lebih cepat untuk beban kerja yang sesuai.
Apa perbedaan antara alokasi sumber daya statis dan dinamis di Spark?
Alokasi statis menetapkan jumlah executor untuk masa pakai job, menawarkan prediktabilitas dengan konsekuensi potensi sumber daya menganggur. Alokasi dinamis memungkinkan Spark meminta atau melepaskan executor berdasarkan task tertunda dan beban kerja, meningkatkan utilisasi klaster untuk job yang berfluktuasi—ideal untuk lingkungan berbagi.
Bagaimana saya harus mengonfigurasi Spark untuk kinerja optimal pada penyimpanan cloud seperti S3?
Aktifkan S3 transfer acceleration, setel spark.hadoop.fs.s3a.connection.maximum, dan gunakan consistent view (S3A v2) untuk menangani eventual consistency. Gabungkan berkas kecil sebelum menulis dan pertimbangkan S3A committer untuk mengurangi overhead operasi list dan meningkatkan throughput tulis.
Bagaimana saya dapat mengamankan komunikasi Spark dengan Kerberos dan TLS?
Aktifkan TLS untuk RPC (spark.ssl.enabled) dan konfigurasikan SASL/Kerberos (spark.authenticate and spark.kerberos.keytab) untuk menegakkan autentikasi mutual. Simpan kredensial dalam keytab yang aman dan dapat diakses HDFS dan batasi akses Spark UI melalui pengaturan ACL untuk mencegah paparan data tanpa izin.
Apa itu Pandas UDF dan kapan lebih efisien dibanding UDF biasa?
Pandas UDF (vectorized UDF) menggunakan Apache Arrow untuk pertukaran data batch antara JVM dan Python, secara drastis mengurangi overhead serialisasi. Mereka mengungguli UDF baris-demi-baris tradisional untuk operasi numerik kompleks, terutama saat memproses batch kolumnar besar di PySpark.
Apa manfaat yang diberikan API DataSource V2 dibanding V1 untuk sumber data kustom?
DataSource V2 menawarkan antarmuka yang lebih bersih dan modular yang mendukung push-down filter, pemangkasan partisi, dan sumber streaming secara native. API ini memungkinkan kontrol baca/tulis yang lebih terperinci dan integrasi yang lebih baik dengan Catalyst optimizer Spark, menghasilkan kinerja lebih tinggi dan kemudahan pemeliharaan untuk konektor kustom.
Saya adalah Cloud Engineer dengan fondasi kuat di bidang Teknik Elektro, machine learning, dan pemrograman. Karier saya dimulai di ranah visi komputer dengan fokus pada klasifikasi citra, sebelum beralih ke MLOps dan DataOps. Saya mengkhususkan diri dalam membangun platform MLOps, mendukung data scientist, dan menghadirkan solusi berbasis Kubernetes untuk menyederhanakan alur kerja machine learning.
