Caching menyimpan partisi hasil perhitungan di memori atau disk sehingga akses berikutnya tidak menghitung ulang lineage-nya. Topik ini membahas cache dan persist, level penyimpanan yang tersedia, perannya dalam mengatasi penghitungan ulang antar action, dan syarat agar cache benar-benar membantu daripada sekadar memakan memori. Setelah membacanya, keputusan menyimpan antara dapat diambil berdasarkan pola pemakaian data, bukan kebiasaan.
Masalah yang dijawab caching#
Pada topik evaluasi lazy telah dibahas bahwa setiap action menghitung ulang seluruh rantai transformasi di atasnya. Perilaku ini dapat diprediksi dan tidak menambah pemakaian memori, tetapi mahal ketika rantai yang sama dihitung berkali-kali, misalnya DataFrame hasil pembacaan dan pembersihan yang dipakai untuk analisis, agregasi, dan join berbeda. Tanpa penyimpanan antara, setiap analisis memutar ulang seluruh pemrosesan dari sumber data.
cache() menyatakan bahwa partisi DataFrame itu layak disimpan setelah pertama kali dihitung, dan penyimpanannya terjadi secara lazy, yaitu baru terisi ketika sebuah action pertama menghitung partisinya. Karena itu pemanggilan cache() tidak berarti DataFrame langsung tersimpan, dan angka pada keluaran skrip berikut memperlihatkan perbedaan status sebelum dan sesudah materialisasi.
cache, is_cached, dan storageLevel#
Skrip berikut menyimpan DataFrame ke cache, memeriksa statusnya sebelum dan sesudah materialisasi, mencetak level penyimpanannya, lalu melepaskannya dengan unpersist().
from pyspark.sql import SparkSession
from pyspark.sql import functions as F
spark = (
SparkSession.builder
.master("local[2]")
.appName("peraga-cache")
.config("spark.sql.shuffle.partitions", "2")
.getOrCreate()
)
df = spark.createDataFrame(
[(1, "Ali", "10A", 85), (2, "Budi", "10A", 90), (3, "Citra", "10B", 78), (4, "Dewi", "10B", 92)],
"id long, nama string, kelas string, nilai long",
)
df.cache()
print("tersimpan cache (belum ada action):", df.is_cached)
print("=== materialisasi pertama ===")
print("jumlah baris:", df.count())
print("level penyimpanan:", df.storageLevel)
print("=== akses kedua memakai cache ===")
df.groupBy("kelas").count().orderBy("kelas").show()
df.unpersist()
print("tersimpan cache setelah unpersist:", df.is_cached)
spark.stop()WARNING: Using incubator modules: jdk.incubator.vector
tersimpan cache (belum ada action): True
=== materialisasi pertama ===
jumlah baris: 4
level penyimpanan: Disk Memory Deserialized 1x Replicated
=== akses kedua memakai cache ===
+-----+-----+
|kelas|count|
+-----+-----+
| 10A| 2|
| 10B| 2|
+-----+-----+
tersimpan cache setelah unpersist: FalsePerlu dicermati bahwa df.is_cached bernilai True bahkan sebelum ada action, karena penanda itu menyatakan niat penyimpanan yang tercatat pada rencana, bukan isi cache yang sudah terisi. Isi cache baru ada setelah count(), dan level penyimpanannya tercetak Disk Memory Deserialized 1x Replicated, yaitu level bawaan cache() yang menempatkan data di memori dengan cadangan disk. Akses kedua berupa agregasi kelas memakai isi yang tersimpan tanpa membaca ulang sumber, dan unpersist() melepaskannya secara eksplisit.
Level penyimpanan pada persist#
cache() adalah singkatan dari persist() dengan level bawaan. Level penuh dipilih dengan df.persist(StorageLevel.X), dan pilihan yang paling sering relevan ada empat. MEMORY_AND_DISK adalah bawaan DataFrame, yaitu memori dulu lalu meluap ke disk. MEMORY_ONLY menyimpan hanya di memori, dan partisi yang tidak muat dibangun ulang dari lineage ketika dibutuhkan. DISK_ONLY menyimpan seluruhnya di disk dan cocok untuk data besar yang pemakaiannya berulang. MEMORY_ONLY_SER menyimpan bentuk terserialisasi yang lebih hemat memori dengan biaya deserialisasi saat dibaca.
Kebiasaan yang sehat adalah tetap pada level bawaan kecuali ada alasan terukur, karena level bawaan sudah menggabungkan kecepatan memori dan keandalan disk. Pemeriksaan isi cache terlihat pada tab Storage di Spark UI, yang dibahas pada topik pemantauan, dan di situlah ukuran cache sesungguhnya dapat diamati alih-alih diperkirakan.
Kapan cache membantu dan kapan merugikan#
Cache membantu pada tiga pola yang jelas. Pertama, DataFrame yang dipakai lebih dari satu kali dalam satu sesi, seperti tabel bersih yang menjadi dasar banyak analisis. Kedua, hasil iteratif yang setiap langkahnya membaca basis yang sama, sebagaimana algoritme pembelajaran mesin. Ketiga, rantai pembacaan yang mahal dari sumber jaringan yang pemakaiannya berulang. Pada ketiganya, biaya satu kali materialisasi dibayar kembali oleh akses-akses berikutnya.
Cache merugikan pada pemakaian sekali jalan, karena data yang disimpan tidak pernah dibaca ulang dan memorinya terbuang. Cache juga merugikan ketika datanya jauh lebih besar dari memori sehingga sebagian besar partisi meluap ke disk. Pada titik itu kecepatan cache mendekati kecepatan pembacaan ulang, sementara tekanan memorinya tetap ada. Kebiasaan menyimpan semua DataFrame di cache adalah pola yang sering menurunkan kinerja justru pada aplikasi yang terasa paling butuh kinerja.
Satu sifat yang juga wajib dipahami adalah kerentanan cache terhadap perubahan rencana. Cache terikat pada rencana DataFrame saat dipanggil, sehingga variabel yang menampung rantai berbeda dianggap DataFrame lain dan membangun cache terpisah. Aturan praktisnya, simpan hasil ke variabel yang bersih sebelum cache(), dan lepaskan dengan unpersist() begitu pemakaiannya selesai.
Kesalahan umum#
Kesalahan pertama adalah mengira cache() segera mengisi memori. Penyimpanan terjadi saat action pertama, sehingga pengukuran waktu materialisasi pertama tetap memuat seluruh komputasi. Pola yang lazim untuk membayar biayanya lebih awal adalah df.cache() diikuti df.count() sebagai pemicu yang disengaja.
Kesalahan kedua adalah lupa unpersist(). Cache yang ditinggal terus menempati memori dan memengaruhi pemilihan level penyimpanan cache berikutnya, dan pada aplikasi panjang akibatnya terakumulasi. Lepaskan cache pada akhir pemakaian, terutama di dalam fungsi yang membuat DataFrame antara.
Kesalahan ketiga adalah menyimpan DataFrame yang isinya sudah dieksplorasi sekali, misalnya melalui show(). Cache bernilai bila ada akses ulang, dan DataFrame antara yang hanya disalurkan ke satu keluaran tidak memenuhi syarat itu. Periksa pola pemakaiannya dahulu, baru putuskan menyimpan.
Ringkasan#
cache()menyatakan niat penyimpanan dan terisi saat action pertama, sehingga statusnya perlu dibedakan antara tercatat dan terisi.- Level bawaan DataFrame adalah
MEMORY_AND_DISK, danpersist(StorageLevel.X)tersedia untuk pilihan memori-disk yang lebih spesifik. - Cache membantu ketika DataFrame dipakai berulang dalam satu sesi, dan merugikan pada pemakaian sekali jalan atau data yang jauh melebihi memori.
- Cache terikat rencana saat dipanggil dan harus dilepas dengan
unpersist()agar memori tidak terpakai diam-diam.
Referensi#
- pyspark.sql.DataFrame.cache — Apache Spark — Dokumentasi cache dan hubungannya dengan persist.
- RDD Persistence — Apache Spark — Penjelasan level penyimpanan dan perilaku eviction yang menjadi dasar persist DataFrame.
- Spark Web UI — Apache Spark — Tempat melihat isi dan ukuran cache pada tab Storage.