Konsep streaming

Model micro-batch, source dan sink.

Daftar isi

Pemrosesan stream menangani data yang terus tiba tanpa akhir, berbeda dengan pemrosesan batch yang bekerja atas kumpulan data yang sudah selesai. Konsekuensinya bukan sekadar data yang panjang, melainkan cara pandang yang berubah, yaitu hasil tidak pernah final dan dihitung ulang setiap kali data baru tiba. Topik ini meletakkan konsep-konsep dasar tersebut sebelum masuk ke sintaks Structured Streaming pada topik berikutnya.

Dari tabel berbatas ke tabel tak berbatas#

Pada pemrosesan batch, tabel diproses secara utuh dan hasilnya ditulis sekali. Pada pemrosesan stream, Structured Streaming memperlakukan aliran data sebagai tabel yang tak berbatas (unbounded table), yaitu tabel yang barisnya terus bertambah seiring waktu. Setiap kali data baru tiba, baris-baris itu dianggap ditempelkan (append) ke tabel tersebut, dan kueri yang didefinisikan di atasnya dihitung ulang untuk menghasilkan keluaran terbaru.

Cara pandang inilah yang membuat API streaming Spark terasa hampir sama dengan API batch. readStream menggantikan read, writeStream menggantikan write, sementara transformasi seperti filter, select, dan groupBy dipakai tanpa perubahan. Perbedaan terpenting ada pada keluaran, karena tabel tak berbatas tidak pernah selesai, sehingga kueri berjalan terus sampai dihentikan secara eksplisit.

Skrip berikut membandingkan dua cara membaca folder yang sama, satu sebagai batch dan satu sebagai stream, tanpa menjalankan pekerjaan apa pun. Perhatikan bahwa hanya tipe pembacaan yang berbeda, sedangkan schema yang dinyatakan untuk keduanya identik.

python peraga_stream.py
import os
import shutil

from pyspark.sql import SparkSession

shutil.rmtree("/tmp/sumber-peraga", ignore_errors=True)
os.makedirs("/tmp/sumber-peraga")
with open("/tmp/sumber-peraga/batch1.txt", "w") as f:
    f.write("kamar1,2026-11-04 08:00:10,28\n")
    f.write("kamar2,2026-11-04 08:00:40,31\n")

spark = (
    SparkSession.builder
    .master("local[2]")
    .appName("peraga-stream")
    .getOrCreate()
)

stream = (
    spark.readStream
    .format("csv")
    .schema("kamar string, waktu timestamp, suhu double")
    .load("/tmp/sumber-peraga")
)
batch = (
    spark.read
    .format("csv")
    .schema("kamar string, waktu timestamp, suhu double")
    .load("/tmp/sumber-peraga")
)

print("readStream adalah stream?", stream.isStreaming)
print("read      adalah stream?", batch.isStreaming)
stream.printSchema()

spark.stop()
text Keluaran
WARNING: Using incubator modules: jdk.incubator.vector
readStream adalah stream? True
read      adalah stream? False
root
 |-- kamar: string (nullable = true)
 |-- waktu: timestamp (nullable = true)
 |-- suhu: double (nullable = true)

Atribut isStreaming menandai jenis pembacaan yang dipakai, dan schema yang dideklarasikan berlaku sama untuk keduanya. Pada streaming, schema harus diberikan secara eksplisit karena folder sumber terus bertambah dan Spark tidak boleh menebak ulang schema setiap kali berkas baru muncul. Penentuan schema ini sekaligus menjadi disiplin yang baik, karena tipe data yang salah baru terlihat saat eksekusi apabila dibiarkan ditebak.

Model micro-batch#

Structured Streaming menjalankan aliran data dalam bentuk micro-batch, yaitu potongan kecil data yang diproses berulang-ulang seperti batch kecil. Setiap potongan berisi seluruh data yang tiba sejak potongan sebelumnya, dan setiap potongan melewati transformasi yang sama. Model ini dipilih karena meminjam seluruh mesin batch Spark, sehingga optimizer dan format berkas yang sama ikut bekerja pada data streaming.

Alternatif model ini adalah continuous processing dengan latensi milidetik, tetapi dukungannya terbatas pada operasi tertentu, dan untuk mayoritas kebutuhan analitik, micro-batch dengan latensi ratusan milidetik sampai beberapa detik sudah memadai. Satu hal yang perlu diluruskan sejak awal adalah anggapan umum bahwa streaming harus cepat. Tujuan Structured Streaming bukan latensi serendah mungkin, melainkan kebenaran hasil pada data yang tiba terus-menerus, dan batas latensinya cukup fleksibel mengikuti trigger yang dipilih.

Skrip pada topik berikutnya memakai trigger availableNow yang memproses seluruh data yang tersedia lalu berhenti. Trigger ini berguna untuk pembelajaran dan pengujian karena hasilnya bisa direkam dan diulang tanpa harus menjaga kueri tetap hidup, sekaligus menjadi jembatan wajar dari kueri batch menuju kueri streaming yang berjalan terus.

Source dan sink#

Source adalah tempat data tiba, sedangkan sink adalah tempat keluaran ditulis. Spark 4 menyediakan source berkas, yaitu folder yang dipantau untuk berkas baru berformat teks, CSV, JSON, Parquet, atau ORC, serta source Apache Kafka untuk aliran pesan dari klaster. Di sisi sink tersedia berkas, konsol untuk pembelajaran, tabel memori untuk pengujian, dan Kafka sebagai sink produksi yang paling umum.

Pada jalur ini, source berkas dipakai untuk seluruh contoh karena dapat direproduksi di satu mesin tanpa infrastruktur tambahan. Folder sumber dipantau, berkas baru yang muncul diproses sebagai potongan berikutnya, dan berkas yang sudah diproses tidak pernah diproses dua kali. Ciri inilah yang membuat urutan berkas menjadi penting, dan seluruh keluaran pada topik-topik berikutnya dapat diulang persis karena urutan berkas sengaja dikendalikan dengan penamaan yang berurutan.

Pilihan sink membawa sedikit perbedaan semantik pada saat menulis. Sink berkas menjamin setiap baris ditulis tepat satu kali dengan bantuan log pencatatan, sedangkan sink konsol dan memori hanya untuk melihat isi, dan keduanya tidak menjanjikan ketahanan. Pembahasan penuh tentang jaminan pengiriman dan peran checkpoint ada pada topik checkpoint dan sink di akhir modul ini.

Kesalahan umum#

Kesalahan pertama adalah membaca folder streaming tanpa menyatakan schema, dengan harapan Spark akan menyimpulkannya seperti pada batch. Spark menolak dengan error yang menyatakan schema wajib untuk source streaming berkas, dan perbaikannya adalah menyatakan schema secara eksplisit pada .schema().

Kesalahan kedua adalah menulis keluaran show() atau collect() pada DataFrame streaming. Aksi itu tidak ada pada tabel tak berbatas, dan Spark memunculkan error yang menyatakan kueri streaming tidak mendukung aksi tersebut. Keluaran streaming harus melewati writeStream menuju sink, dan inspeksi dilakukan melalui isi sink itu sendiri.

Kesalahan ketiga adalah menyamakan folder source dengan folder sink. Berkas yang ditulis ke dalam folder yang sedang dipantau akan ikut dibaca sebagai data, sehingga pipeline yang sink-nya menulis ke folder source-nya sendiri memproses keluarannya kembali tanpa henti. Pisahkan direktori sumber dan direktori keluaran sejak skrip pertama.

Ringkasan#

  • Structured Streaming memperlakukan aliran data sebagai tabel tak berbatas yang barisnya terus bertambah, dengan API yang nyaris sama dengan pemrosesan batch.
  • Model eksekusinya adalah micro-batch, yaitu potongan kecil data yang diproses berulang memakai mesin batch yang sama.
  • Source dan sink menentukan dari mana data tiba dan ke mana keluaran ditulis, dan source berkas adalah pilihan yang paling mudah direproduksi untuk belajar.
  • Schema pada source streaming wajib dinyatakan eksplisit karena folder sumber terus bertambah.

Referensi#