Kurs
Hiç aniden başarısız olan bir Spark işini hata ayıklamaya çalışıp, Spark tavşan deliğinin ne kadar derin olduğunu fark ederek tamamen kaybolduğunuz oldu mu?
İlk kez Apache Spark ile çalıştığımda, birkaç PySpark dönüşümü yazmam gerektiğini ve Spark’ın kümeye “büyülü” bir şekilde ölçekleneceğini düşündüm. Yanılmışım. Spark’ın performansı, perde arkasında neler olduğunun anlaşılmasına tamamen bağlıdır.
Bu rehber, Spark’ı bir kara kutu gibi görmek istemeyen herkes için. Spark’ın mimarisinin nasıl tasarlandığını; ana-işçi modelinden yürütme iş akışına, bellek yönetiminden hata toleransı mekanizmalarına kadar adım adım inceleyeceğiz.
Hızlı, hataya dayanıklı ve verimli büyük veri uygulamaları geliştirmek istiyorsanız doğru yerdesiniz!
Apache Spark’ın Temel Mimarisi
İlk PySpark satırınızı yazmadan önce bile Spark sizin için bazı mimari kararlar almış olur. Spark yalnızca bellek içi hesaplama sayesinde hızlı değildir; aynı zamanda, düğüm çöküşleri, Java Virtual Machine (JVM) sorunları ve tutarsız veri hacimleri gibi gerçek dünya kaosuna dayanabilen, ölçeklenen bir ana-işçi mimarisi üzerine kuruludur.
Spark’ın çekirdek mimarisini ve modern büyük veri iş akışlarında neden hâlâ bu kadar güçlü ve yaygın olduğunu parçalayalım.
Ana-işçi paradigmaları
Spark’ın merkezinde ana-işçi modeli vardır. Şöyle düşünün:
- Sürücü (ana): Spark’ın beynidir.
main()fonksiyonunuzu çalıştırır, Spark bağlamını oluşturur, DAG zamanlamasını yönetir ve kümeye ne yapacağını söyler. - Yürütücüler (işçiler): Bunlar kaslardır. Görevlerinizi yürütür, veriyi bellekte tutar ve sürücüye rapor verirler.
Bu kurulum sayesinde dönüşümleri tanımlamaya odaklanırsınız; Spark ise bunları yürütücüler üzerinde paralel olarak nerede ve nasıl çalıştıracağını belirler.
Bu tasarımda sevdiğim şeylerden biri, dağıtımdan bağımsız olmasıdır. Aynı kod, yerel makinenizde, Kubernetes ya da Mesos üzerinde dağıtmanızdan bağımsız olarak çalışır. Bu da yerelde geliştirme ve test etmeyi kolaylaştırır, ardından kodunuzu yeniden yazmadan kümelere ölçeklemenize imkân tanır.
Spark’ın sürücü-işçi ayrımının bir diğer güçlü faydası: Hata yalıtımını iyileştirir. Bir işçi düğümü bir görevi yürütürken ölürse, Spark uygulamanızı çökertmeden o görevi başka bir işçiye yeniden atayabilir.
Çekirdek bileşenler
Sürücüde ve düğümlerde neler olduğuna bakalım.

Spark mimarisi. Görsel: yazar.
Spark bağlamı
SparkContext() çağırdığınızda veya SparkSession.builder.getOrCreate() kullandığınızda, Spark’ın tüm iç sihrine açılan kapıyı aralarsınız.
Spark bağlamı şunları yapar:
- Küme yöneticinize bağlanır
- Yürütücüleri tahsis eder
- İş durumunu ve yürütme planlarını takip eder
Spark, perde arkasında bir Yönlendirilmiş Çevrimsiz Grafik (DAG) oluşturur. Bu DAG, aşamalara ve görevlere bölünür ve ardından paralel olarak yürütülür.
DAG zamanlayıcı, hangi görevlerin birlikte çalıştırılabileceğini bulur; Görev zamanlayıcı ise bunları yürütücülere atar. Bu sırada Blok yöneticisi, verinin gerektiğinde önbelleğe alınmasını, shuffle edilmesini veya yeniden yüklenmesini sağlar.
Bu katmanlı tasarım, belleği, depolamayı ve hesaplamayı bağımsız olarak ince ayar yapabilmenizi sağlayarak Spark’ı inanılmaz esnek kılar.
Spark dönüşümleri veya özellik mühendisliği ile çalışıyorsanız, bu mimariyi iş başında görmek için PySpark ile Özellik Mühendisliği kursuna göz atın.
Yürütücü çalışma zamanı
İşin yapıldığı yer yürütücülerdir.
Her yürütücü şunları çalıştırır:
- Bir veya daha fazla görev (iş parçacıklı)
- Veriyi önbelleğe almak ve shuffle çıktısını tutmak için bir miktar bellek
- Diğerlerinden yalıtılmış kendi JVM örneği
Her yürütücünün ne kadar bellek alacağını, kaç çekirdek kullanacağını ve bellek tükendiğinde diske yazıp yazmayacağını yapılandırabilirsiniz.
Ancak dikkatli olun: Yeterince bellek ayırmazsanız sürekli bellek yetersiz hataları alırsınız. Öte yandan çok fazla bellek ayırmak da kaynak israfıdır. İzleme ve ayarlama burada kritik önemdedir.
Yürütme İş Akışı: Koddan Kümeye
PySpark kodu yazmak oldukça basit hissettirir. Bir DataFrame’i filtrelersiniz, bir join yaparsınız, bir şeyleri toplarsınız ve çalıştırırsınız. Ancak o temiz API’nin arkasında Spark, işi birden fazla düğüme yayabilen bir yürütme motorunu sessizce ayağa kaldırır.
Perde arkasında neler olduğuna birlikte bakalım.
Mantıksal plandan fiziksel plana dönüşüm
Çoğu Spark kullanıcısının başlangıçta fark etmediği şey şu: PySpark kodu yazdığınızda, hemen hiçbir şey çalışmıyor. Bir plan inşa ediyorsunuz ve Spark’ın Catalyst Optimize Edicisi bu planı alıp verimli bir yürütme stratejisine dönüştürüyor.
Dört aşamada çalışır:
- Analiz: Spark sütun adlarını, veri tiplerini ve tablo referanslarını çözümler; her şeyin geçerli olduğundan emin olur.
- Mantıksal optimizasyon: Spark bu aşamada öngörü itme (predicate pushdown) ve sabit katlama (constant folding) gibi kuralları uygular. Filtreleri optimize eder ve projeksiyonları birleştirir.
- Fiziksel planlama: Spark birden fazla yürütme stratejisini değerlendirir ve (veri boyutu, bölümleme vb. temelinde) en verimlisini seçer.
- Kod üretimi: Son olarak tüm-aşama kod üretimi kullanarak JVM bytecode üretir.

Spark’ın Catalyst Optimize Edicisi. Görsel: Databricks.
Yani o .select(), .join() ve .groupBy() zinciri satır satır çalışmıyor. Analiz ediliyor, optimize ediliyor ve kümede hızlı çalışacak şekilde derleniyor.
En kullanışlı PySpark komutları için bir kopya kâğıdına ihtiyacınız varsa, şu PySpark Kopya Kâğıdına göz atın.
DAG zamanlayıcı ve aşama oluşturma
Plan tamamlandığında, işi DAG zamanlayıcı devralır.
İşi, shuffle sınırlarına göre aşamalara böler; burada Spark, sırayla ne olacağına ve paralel olarak neyin yürütülebileceğine karar verir.
İki ana aşama türü vardır:
- ShuffleMapStage: Bu, genellikle
groupBy()veyajoin()gibi geniş dönüşümlerin tetiklediği bir shuffle içerir. Veri bölümlenir ve ağ üzerinden gönderilir. Bu aşama türü, ResultStage’i hesaplamak için gereklidir. - ResultStage: Disk yazımı ya da sonuçların sürücüye dönmesi gibi çıktılar üreten aşamalardır.
Öğrendiğim temel şeylerden biri shuffle’ları en aza indirmektir. Bir aşama bitmeden önce bir shuffle gerçekleşmek zorundadır ve pahalıdır. DAG’inizde nerede ortaya çıktıklarını ve shuffle sayısını azaltmak için kodunuzu daha fazla optimize edip edemeyeceğinizi anlamanız gerekir.
Görev yürütme yaşam döngüsü
DAG zamanlayıcı tüm aşamaları oluşturduktan sonra, bunlar farklı yürütücüler üzerinde yürütülebilir.
Görev yürütme yaşam döngüsü kabaca şöyledir:
- Görev serileştirme: Sürücü, görev talimatlarını serileştirir ve yürütücülere gönderir.
- Shuffle yazma aşaması: Spark, bölümlenmiş çıktıyı yerel diske yazar.
- Getirme aşaması: Sonraki aşamadaki yürütücüler, ilgili shuffle dosyalarını küme genelindeki diğerlerinden çeker.
- Seriden çıkarma ve yürütme: Yürütücüler veriyi seriden çıkarır, mantığınızı çalıştırır ve gerekirse sonuçları önbelleğe alır veya yazar.
- Çöp toplama: JVM, Spark uygulamaları tarafından artık kullanılmayan belleği otomatik olarak geri kazanır. Bu adım, bellek sızıntılarını önlemek ve uygulamaların sorunsuz çalışmasını sağlamak için kritiktir.
Kendi deneyimimden küçük bir ipucu: Spark işiniz daha önce iyi çalışırken bir anda takılı kalırsa, çoğunlukla çöp toplama veya shuffle getirme gecikmelerindendir. Kodunuzu her zaman kontrol edin ve bu konuları etkili şekilde optimize edebilmek için Spark mimarisini anladığınızdan emin olun.
Bellek Yönetimi Mimarisi
Spark’ın bellek yönetimi oldukça karmaşık bir konudur ve anlamazsanız saatlerinizi hata ayıklamaya mal edebilir.
Bu yüzden Spark’ın kaputun altında belleği nasıl yönettiğine bakalım ki bunun farkında olun ve yavaş kod ya da bellek yetersiz hataları için saatler harcamaktan kaçının.
Birleşik bellek modeli
Spark 1.6’dan önce bellek, yürütme (shuffle ve join’ler için) ve depolama (önbellekleme için) arasında katı şekilde bölünmüştü. Spark 1.6 ile birleşik bellek modeli geldi ve bu değişti.
Birleşik bellek modelinde, bellek üç ana havuza ayrılır:
- Ayrılmış bellek: Spark’ın iç bileşenleri ve sistem için kullanılan küçük bir miktar bellek.
- Spark belleği: Yürütme verilerini depolamak ve önbellekleme için kullanılır. Dinamik olarak paylaşılır. İşiniz shuffle için daha fazla, önbellekleme için daha az bellek (ya da tersi) gerektiriyorsa Spark uyum sağlar.
- Kullanıcı belleği: Spark uygulamalarında kullanıcı kodunu yürütmek için gereken kullanıcı tanımlı veri yapıları için alan.
Spark bellek havuzu iki alt havuza daha ayrılır:
- Yürütücü belleği: Görevleri işlerken aşamalar sırasında gereken geçici verileri saklar (örn. shuffle’lar, join’ler, toplulaştırmalar, …).
- Depolama bellek havuzu: Veriyi önbelleğe almak ve dahili veri yapılarını saklamak için kullanılır.
Bu esneklik, öngörülemeyen veri hacimleriyle Spark’ın daha esnek olmasını sağlar.
Ancak bu aynı zamanda, perde arkasında ne olduğunu bilmediğinizde biraz kontrol kaybı demektir. Örneğin, büyük bir DataFrame’i cache() ettiğinizde fakat aynı aşamada pahalı toplulaştırmalar da varsa, Spark shuffle için yer açmak adına önbelleğe aldığınız veriyi tahliye edebilir.
Yığın dışı ve kolonar depolama
Spark’ın yığın dışı ve kolonar depolamasında Tungsten motoru devreye girer.
Tungsten, Spark’ın performansını iyileştiren birkaç optimizasyon getirmiştir:
- Yığın dışı bellek yönetimi: Spark, bazı verileri artık JVM yığını dışında saklayarak çöp toplama yükünü azaltır ve bellek yönetimini daha öngörülebilir kılar.
- İkili format depolama: Veriler, CPU önbellek dostu, sıkı bir ikili biçimde saklanır; bu da CPU kullanımını iyileştirir ve vektörleşmiş yürütmeyi mümkün kılar.
- Önbellek farkındalığı olan algoritmalar: Spark, CPU önbelleklerini daha verimli kullanarak RAM veya diskten gereksiz okumaları önleyebilir.
Ve DataFrame’lerle çalışıyorsanız, bu optimizasyonları zaten perde arkasında kullanıyorsunuz. Bu yüzden insanları ham RDD’ler yerine DataFrame ve SQL API’lerine yönlendirmemin sebeplerinden biri de budur. Ek bir ayara gerek kalmadan Catalyst ve Tungsten’in tüm gücünden yararlanırsınız.
Veri temizleme hatlarıyla çalışıyorsanız, bunu PySpark ile Veri Temizleme kursunda uygulamalı olarak göreceksiniz.
Hata Toleransı Mekanizmaları
Dağıtık sistemlerle çalışıyorsanız bir gerçeği bilirsiniz: Arızalanırlar. Düğümler çöker. Ağ hataları olur. Yürütücüler belleksiz kalır ve kapanır.
Ancak Spark, bu sorunları ele alacak şekilde inşa edilmiştir ve işlerinizin yine de başarıyla tamamlanmasını sağlar.
Hadi, bazı dengesizlikler oluşsa bile Spark’ın işlerinizi nasıl başarıyla tamamladığını daha derinlemesine inceleyelim.
RDD soyağacı (lineage) takibi
Dayanıklı Dağıtılmış Veri Kümeleri (RDD’ler), Spark’taki temel veri yapısıdır. Ve dayanıklı olarak anılmalarının bir nedeni vardır.
Spark, bir düğüm hatası ve veri kaybı durumunda her RDD’nin yeniden hesaplanabilmesini sağlamak için soyağacı bilgisini kullanır.
Yani bir düğüm başarısız olduğunda Spark, soyağacı grafiğini kullanarak kaybolan veriyi basitçe yeniden hesaplar.
Pratikte nasıl çalıştığı şöyledir:
- Dar bağımlılıklar (ör.
map()veyafilter()): Spark yalnızca kaybolan bölümü yeniden hesaplamak zorundadır. - Geniş bağımlılıklar (ör.
groupBy()veyajoin()): Birden çok bölümden veri almak gerekebilir; çünkü birden fazla aşamanın çıktısı gerekebilir.
Soyağacı, hataları elle ele alma ihtiyacını ortadan kaldırır. Ancak soyağacı grafiğiniz çok uzarsa—yüzlerce dönüşüm içerebilir—kaybolan veriyi yeniden hesaplamak pahalı hâle gelir. İşte burada checkpointing devreye girer.
Checkpointing ve write-ahead loglar
Karmaşık iş akışları veya akış (streaming) işleriyle karşılaştığınızda Spark yalnızca soyağaçına güvenemez. Burada checkpointing devreye girer.
rdd.checkpoint() çağırarak mevcut RDD durumunu güvenilir bir depolama konumuna (HDFS gibi) kalıcı hâle getirebilirsiniz.
Spark sonra soyağacını kısaltır. Bir hata olursa, yeniden hesaplamak yerine veriyi doğrudan yeniden yükler.
Yapılandırılmış akışta Spark, verinin aktarım sırasında kaybolmamasını sağlamak için write-ahead logları (WAL) da kullanır.
Onu bu kadar sağlam yapan da budur:
- Güvenilir alıcılar: Gelen veriyi işlemeden önce loglara yazarlar.
- Yürütücü kalp atışları: Bu düzenli sinyaller, yürütücülerin canlı ve sağlıklı olduğunu teyit eder.
- Checkpoint dizinleri: Akış işleri için ofsetleri, metaveriyi ve çıktı durumunu tutar; böylece kaldığınız yerden devam edebilirsiniz.
Batch işlemleri için checkpointing isteğe bağlıdır; ancak akış hatlarında zorunludur.
Düşünün ki bir Spark işiniz 10 saat çalıştıktan sonra başarısız oldu; ancak checkpointing ve WAL’lar sayesinde kaldığınız yerden devam edebiliyorsunuz.
Gelişmiş Mimari Özellikler
Şimdiye kadar Spark’ın işleri nasıl işlediğini, bellek ve hataları nasıl ele aldığını gördünüz.
Bu bölümde, Spark’ı daha dinamik, daha gerçek zamanlı ve daha uyarlanabilir kılan bazı gelişmiş mimari yükseltmelere dalıyoruz.
Uyarlamalı sorgu yürütme (AQE)
AQE, Spark 3.0’da tanıtıldı ve yürütme sırasında toplanan istatistiklere dayanarak yürütme planlarını çalışma anında dinamik olarak ayarlayarak sorgu performansını artırır.
AQE’nin özellikleri şunlardır:
- Join stratejilerini dinamik olarak değiştirme: Yayın (broadcast) join belleğe sığmayacaksa, AQE sort-merge join’e geçer.
- Shuffle bölümlerini birleştirme: Küçük shuffle bölümlerini daha büyük parçalara birleştirerek ek yükü azaltır.
- Eğik (skewed) veriyi ele alma: AQE, eğik bölümleri bölerek yürütme süresini dengeler.
Bu özellik oyunun kurallarını değiştirir; çünkü önceden manuel ayar ve deneme-yanılma gerektiren işler, gerçek zamanlı uyum sağlayabilir.
Sadece yapılandırmadan açıkça etkinleştirdiğinizden emin olun (spark.sql.adaptive.enabled = true). Ve Spark 3.0+ kullanıyorsanız bunu kapalı tutmak için bir neden yok.
Yapılandırılmış akış mimarisi
Structured Streaming, Spark’ın motorunu yeni bir API öğrenmenizi gerektirmeden gerçek zamanlı alana genişletir.
Perde arkasında hâlâ mikro-batch uygular. Ancak şunları ele alır:
- Ofset yönetimi: Spark, kaynağınızdan (Kafka, soket, dosya vb.) hangi verinin okunduğunu tam olarak takip eder. Doğru yapılandırıldığında güçlü tam-bir-kez (exactly-once) garantileri sunar.
- Su işaretleme (watermarking): Zaman tabanlı toplulaştırmalarda Spark, geç gelen verinin ne zaman “çok geç” olduğuna su işaretleriyle karar verir. Bu, olay zamanı işlem için kritiktir.
- Durum depoları: Pencereli toplulaştırmalar veya akış join’leri yaptığınızda Spark, mikro-batch’ler arasında durumu korur. Bu durum diskte saklanır ve veri kaybını önlemek için checkpoint edilir.
Burada güçlü olan, akışın batch gibi hissettirmesidir. Bir groupBy() ya da filter() yazarsınız, Spark gerisini halleder; böylece uzmanlaşmış bir araç zincirine gerek kalmadan akış analitiği erişilebilir olur.
Güvenlik Mimarisi
Spark’ı üretimde çalıştırıyorsanız—özellikle finans, sağlık veya benzeri alanlarda—Spark’ın kimlik doğrulama, şifreleme ve denetlenebilirliği nasıl ele aldığını bilmeniz gerekir.
Haydi bu konulara ve Spark’ın bunları nasıl sağladığına daha yakından bakalım.
Kimlik doğrulama ve şifreleme
Spark’ın önce etkinleştirmeniz gereken birçok güvenlik özelliği vardır. Fakat etkinleştirdikten sonra, güvenli iletişim ve kimlik doğrulama için sağlam bir araç seti sunar:
- Kimlik doğrulama (SASL): Spark, yalnızca yetkili kullanıcı ve servislerin iş göndermesini veya kümeye bağlanmasını doğrulamak için Basit Kimlik Doğrulama ve Güvenlik Katmanı’nı (SASL) kullanır.
- Aktarımda şifreleme (AES-GCM, SSL/TLS): Spark, düğümler arasındaki iletişimi AES-GCM (kimlik doğrulamalı şifreleme) veya TLS ile şifreler. Bu, özellikle çok kiracılı veya bulut ortamlarında iş verisinin dinlenmesini engeller.
- Kerberos entegrasyonu: Hadoop/YARN üzerinde çalışıyorsanız Spark, güvenli kullanıcı kimlik doğrulaması için Kerberos ile entegre olur. Bu, Spark işlerinizin doğrudan kurumsal kimlik ve erişim yönetimi sistemlerine bağlanmasını sağlar.
- UI erişim kontrolü: Spark Web UI, (loglar, giriş yolları, SQL sorguları gibi) hassas bilgileri sızdırabilir; bu nedenle
spark.acls.enable=truevespark.ui.view.aclsilespark.ui.view.acls.groupsayarlarını yapılandırarak erişimi kısıtlayın.
Tüm güvenlik özelliklerini Spark’ın resmî dokümantasyonunda inceleyebilirsiniz. Göz atın ve Spark uygulamalarınızı güvenceye almak için ihtiyaç duyduğunuz özellikleri etkinleştirdiğinizden emin olun.
Denetim ve uyumluluk
Kimin neyi ne zaman yaptığını kaydetmek de kritik önemdedir.
Spark şunları destekler:
- Olay günlüğü: Etkinleştirildiğinde (
spark.eventLog.enabled=true) Spark, her iş, aşama ve görev olayını diske kaydeder. Bu logları iş geçmişini yeniden oynatmak veya denetim gerekliliklerini karşılamak için kullanabilirsiniz. - Rol tabanlı erişim kontrolü (RBAC): Spark RBAC sunmaz; ancak Databricks, EMR veya OpenShift gibi bir platform üzerinden Spark kullanıyorsanız genelde altyapı katmanında RBAC elde edersiniz. Spark, tanımlı bir kimlikle iş gönderir; bu kimlik hem veriye hem hesaplama kaynaklarına erişimi kontrol eder.
- Kaynakta veri maskeleme ve erişim kontrolü: Spark, (Parquet, Delta Lake, Hive, vb.) birçok kaynaktan okur; erişim kontrolünüzün orada uygulanması gerekir.
Performans Optimizasyon Kalıpları
Spark oldukça güçlü ve hızlıdır; nerede gerekli ayarları yapacağınızı bilirseniz daha da hızlandırılabilir.
Spark’tan en iyi verimi almak için optimize etmeyi deneyebileceğiniz birkaç alan vardır. Gelin her birine daha yakından bakalım.
Shuffle optimizasyonu
Spark’ın zayıf noktası varsa, o da shuffle’dır. Shuffle, verinin bölümler arasında taşınması gerektiğinde gerçekleşir; genellikle groupByKey(), distinct() veya join() gibi geniş dönüşümlerin ardından.
Shuffle’lar ters giderse devasa disk I/O, uzun çöp toplama duraklamaları veya asla bitmeyen eğik görevler yaşayabilirsiniz.
Shuffle’ları şöyle iyileştirebilirsiniz:
- Tercih edin:
groupByKey()yerinereduceByKey():reduceByKey()shuffle’dan önce yerelde toplar.groupByKey()her şeyi ağa gönderir. - Akıllıca yeniden bölümleyin: Paralelliği artırmak için
.repartition(n)veya azaltmak için.coalesce(n)kullanın. Varsayılan bölümlemeyi Spark’a bırakmayın. - Yayın join’leri (dikkatle) kullanın: Veri kümelerinden biri yeterince küçükse, tüm işçilere yayınlayın. Boyut sınırını kontrol etmek için
spark.sql.autoBroadcastJoinThresholdayarlayın. - Kaçının:
collect(): Mümkün olduğunca kaçının; veriyi sürücüye çekmek performansı öldürür.
Bellek yapılandırma yönergeleri
Spark’ın belleğini ayarlamak başlı başına bir bilim olabilir; ancak aşağıdaki kontrol listesini kullanarak işi kolaylaştırabilirsiniz:
- Yeterli bellek ayırın: Spark kümesi için en az 6 GB bellekle başlayın ve özel ihtiyaçlarınıza göre ayarlayın.
- Spark bellek payını dikkate alın: Varsayılan olarak Spark’ta bellek payı %60’tır. Uygulamalarınız DataFrame/Dataset işlemlerine ağır şekilde dayanıyorsa veya daha fazla kullanıcı belleğine ihtiyacınız varsa artırın.
- Yürütücü başına doğru çekirdek sayısını kullanın: Genellikle 3-5 idealdir. Çok azı yetersiz kullanım, çok fazlası görev çekişmesine yol açar.
- Dinamik tahsisi etkinleştirin (destekleniyorsa): Spark, iş yüküne göre yürütücüleri artırıp azaltabilir.
spark.dynamicAllocation.enabled=true
spark.dynamicAllocation.minExecutors=2
spark.dynamicAllocation.maxExecutors=20
- Depolama payını ayarlayın: Daha fazla önbelleğe ihtiyacınız varsa
spark.memory.storageFractiondeğerini artırın. - Bellek kullanımını izleyin ve profilleyin: Bellek tüketimini takip etmek ve darboğazları belirlemek için Spark UI veya VisualVM gibi araçları kullanın.
Bellek yapılandırmasını ayarlamak ciddi fark yaratabilir. Bir keresinde tek satır kod değiştirmeden, yalnızca bellek yapılandırmasını uyarlayarak 30 dakikalık bir işi 8 dakikaya indirdim.
Küme boyutlandırma formülleri
Ekiplerin çoğu bu kısmı yanlış yapar; çünkü doğru tahmin etmek yerine küme boyutunu kafadan atarlar.
Ancak aşağıdaki formülleri kullanarak daha iyisini yapabilirsiniz:
- Bölüm sayısını belirleyin:
- Veri boyutunuz ve hedef bölüm boyutunuza göre gereken bölüm sayısını hesaplayın.
- Standart bir kılavuz, sıkıştırılmamış veri için bölüm başına 128 MB ila 256 MB’tır.
- Formül: Bölüm Sayısı = Yukarı yuvarla(Total Veri Boyutu ÷ Bölüm Boyutu).
- Toplam çekirdek sayısını hesaplayın:
- Gereken çekirdek sayısı, tüm bölümleri paralel olarak işleyebilecek kadar olmalıdır.
- Formül: Toplam Çekirdek = Yukarı yuvarla(Bölüm Sayısı ÷ Çekirdek başına Bölüm).
- Yürütücü başına belleği belirleyin:
- Her yürütücünün, çekirdekleri, bölüm boyutu ve ek yük temelinde ne kadar belleğe ihtiyaç duyduğunu hesaplayın.
- Formül: Yürütücü Başına Bellek = Taban Bellek × (1 + Ek Yük Yüzdesi).
- Yürütücü sayısını hesaplayın:
- Toplam çekirdek sayısı ve yürütücü başına çekirdeğe göre yürütücü sayısını belirleyin.
- Formül: Yürütücü Sayısı = Yukarı yuvarla(Toplam Çekirdek ÷ Yürütücü Başına Çekirdek).
- Toplam belleği hesaplayın:
- Küme için gereken toplam belleği, yürütücü sayısı ve yürütücü başına belleğe göre hesaplayın.
- Formül: Toplam Bellek = Yürütücü Sayısı × Yürütücü Başına Bellek.
Örneğin:
- Girdi: ~128MB bölüm boyutuyla 500GB veri
- Bölümler: ~4.000 bölüm
- Çekirdekler: 4.000 bölüm / çekirdek başına 4 bölüm = 1.000
- Yürütücü başına bellek: Yürütücü başına 8 GB ve %20 ek yük varsayın. 8 GB * 1,20 = 9,6 GB
- Yürütücüler: 1.000 çekirdek / yürütücü başına 4 çekirdek = 250 yürütücü
- Toplam bellek: 250 yürütücü * 9,6GB = 2.400 GB
Ama unutmayın: Bu yalnızca bir tahmindir. Bir başlangıç noktası olarak kullanın ve sonra profilleyerek daha fazla optimize edin.
Yükselen Mimari Eğilimler
Spark on yıldır hayatımızda; ancak hâlâ oldukça güncel. Bulut-yerel platformlar, GPU hızlandırma ve daha sıkı ML entegrasyonu sayesinde her zamankinden hızlı evriliyor.
Spark’ı bugün üç yıl önce kullandığınız gibi kullanıyorsanız, muhtemelen hem performans kaybediyor hem de harika yeni özellikleri kaçırıyorsunuz.
En yenilerine bir göz atalım.
Photon motoru (Databricks)
Databricks ile çalışıyorsanız muhtemelen Photon ile çalıştınız ve duydunuz.
Databricks hakkında daha fazla bilgi edinmek isterseniz Introduction to Databricks kursunu öneririm.
Photon, Databricks Lakehouse platformunda düşük maliyetle hızlı sorgu performansı sunan yeni nesil motordur. Spark API’leriyle uyumludur; dolayısıyla ondan yararlanmak için Spark kodunuzu uyarlamanız gerekmez.
SQL ve PySpark kodunuzu önemli ölçüde hızlandırmaya yardımcı olur.
Photon şu özellikleri içerir:
- Vektörleştirilmiş yürütme: Photon veriyi kolonar paketler halinde işler, aynı anda birden çok değer üzerinde işlem yapmak için SIMD (Single Instruction, Multiple Data) CPU talimatlarını kullanır. Geleneksel Spark satır-satır yürütme kullanır ve bellek tahsisi ile çöp toplamayı büyük ölçüde JVM’e bırakır.
- C++ çalışma zamanı (JVM ek yükü yok): Büyük Spark işlerinde darboğaz olabilen Java çöp toplamaya gerek kalmaz. Bellek C++ ile hassas şekilde yönetilir.
- Geliştirilmiş sorgu optimizasyonları: Photon, Spark’ın Catalyst Optimize Edicisi ile derin entegrasyona sahiptir; ayrıca yürütme sırasında (çalışma zamanı filtreleme, uyarlamalı kod yolları, join ve toplulaştırma optimizasyonları gibi) kendi optimizasyonlarını da içerir.
- Donanım hızlandırma: Modern donanım desteği (NVIDIA GPU’lar, Intel CPU’lar için AVX-512 komut setleri, AWS’de Graviton (ARM) işlemciler).
Sunucusuz Spark
Sunucusuz harikadır; çünkü kümeleri yönetmek, kaynakları önceden sağlamak zorunda kalmazsınız ve yalnızca Spark çalıştığı süre için ödeme yaparsınız.
Ve Spark için sunucusuz yaklaşım Databricks Serverless, AWS Glue ve GCP Dataproc Serverless gibi servislerde hâlihazırda mevcuttur.
Ve işte neden etkileyici olduğuna dair birkaç nokta:
- Otomatik ölçekleme: Platform, işinizin gerçek ihtiyaçlarına göre hesaplamayı ölçekler; kaç düğüme ihtiyaç duyduğunuzu tahmin etmeniz gerekmez.
- Maliyet etkinliği: Yalnızca kullandığınız kadar ödersiniz. Boşta duran sunucular için ödeme yok.
- Sadelik: Küme kurulumu, yapılandırması ve bakımıyla uğraşmanıza gerek yoktur; sizin için halledilir.
- Performans: Kurulum ve yapılandırma sizin için optimize edildiğinden daha hızlı yürütme süreleri mümkündür.
Sunucusuz Spark, etkileşimli analitik, ad-hoc işler veya öngörülemeyen iş yükleri için idealdir.
Ancak dikkat: Uzun süreli, tutarlı hatlar sabit kümelerde hâlâ daha ucuz olabilir. Hem maliyeti hem gecikmeyi mutlaka ölçün.
MLflow entegrasyonu
Endüstri dönüşürken veri mühendisliği ile yapay zekâ arasındaki çizgi bulanıklaşıyor. DataFramed podcastte Azurelib Academy’nin CEO’su & Kurucusu Deepak Goyal’ın da incelediği gibi
Veri mühendisliği, yakında gerçekleşecek yapay zekâ dönüşümünde hayati ve temel bir rol oynayacak.
Deepak Goyal, CEO & Founder at Azurelib Academy
Ölçekte makine öğrenimi yapıyor ve modelleri üretime almayı hedefliyorsanız, Spark tek başına yeterli değildir. Deney takibi, model versiyonlama ve yeniden üretilebilirlik gibi MLOps ilkelerine ihtiyacınız var. İşte MLflow burada devreye girer.
MLflow artık Spark ile entegre olur ve hatlarınıza tam bir MLOps yığını getirir.
Şunları yapabilirsiniz:
- Deneyleri takip edin: Spark ML işlerinden parametreleri, metrikleri ve eserleri
mlflow.log_param()vemlflow.log_metric()kullanarak kaydedin. - Modelleri versiyonlayın:
pyspark.mlveyasklearnmodellerini doğrudan MLflow’un model kayıt defterine kaydedin. - Modelleri sunun: Eğitilmiş modelleri MLflow’un model sunum özelliğiyle REST uç noktalarına dağıtın.
Araç değiştirmenize gerek yok. Eğitme, özellik mühendisliği ve skorlama için Spark kullanmaya devam ederken, MLOps görevleri için MLflow’dan yararlanırsınız.
Sonuç
Spark hakkında fazla bir şey bilmiyorsanız o, dev bir kara kutu gibidir. Biraz PySpark kodu yazarsınız, çalıştırırsınız ve umarsınız ki çalışsın.
Bazen bu benim için iyi çalıştı; bazen de uzun hata ayıklama seanslarına ve neyin ters gittiğini bulmaya yol açtı.
Perde arkasına bakmaya başlayana kadar her şey anlam kazanmamıştı. Ve neler olup bittiğini anlamam epey zaman aldı.
Baştan başlasaydım odaklanacağım noktalar şunlar olurdu:
- Spark’ın kodunuzu işleri, aşamaları ve görevlere nasıl böldüğünü öğrenin.
- Belleği anlayın.
- Shuffle’lara dikkat edin.
- Küçük başlayın ve yerel modda çalıştırın. Ellerinizi kirletin.
Bu makalede tam olarak bunları öğrendik.
Öğrenmeye devam etmek isterseniz, başlangıç seviyesine uygun şu kaynakları öneririm:
- Introduction to PySpark: Hâlâ alışma aşamasındaysanız harika bir uygulamalı başlangıç noktası.
- Cleaning Data with PySpark: Veriyi temizlemeyi öğrenin; çünkü gerçek dünya verisi her zaman dağınıktır.
- The Top 20 Spark Interview Questions: Yalnızca mülakatlar için değil, anlayışınızı derinleştirmek için de.
- Top 4 Apache Spark Certifications in 2025: Yeteneklerinizi sertifikalarla belgelemek isterseniz.
SSS
Spark dağıtımım için doğru küme yöneticisini nasıl seçerim?
Spark birden çok küme yöneticisini (YARN, Mesos, Kubernetes ve standalone) destekler. Seçiminiz mevcut altyapıya, kaynak paylaşım ihtiyaçlarına ve operasyonel uzmanlığa bağlıdır: YARN, Hadoop kümeleriyle iyi entegre olur; Kubernetes konteynerleştirilmiş taşınabilirlik sunar; Mesos çok kiracılı izolasyonda başarılıdır.
Harici shuffle servisi nedir ve performansı nasıl iyileştirir?
Harici shuffle servisi, shuffle dosyası sunumunu yürütücü yaşam döngülerinden ayırır; bu da dinamik tahsisi mümkün kılar ve yürütücülerin sonlandırılması sırasında veri kaybını azaltır. Yürütücüler kapandıktan sonra bile shuffle dosyalarını erişilebilir tutarak aşama yeniden denemelerini hızlandırır ve ağır yük altında disk I/O’yu azaltır.
Yayın (broadcast) join’leri dahili olarak nasıl çalışır ve ne zaman kullanılmalıdır?
Yayın join’lerinde Spark, küçük bir arama tablosunu tam veri shuffle’ını önlemek için her yürütücüye gönderir. Birleştirmenin bir tarafı spark.sql.autoBroadcastJoinThreshold (varsayılan 10 MB) altında olduğunda kullanın; bu, ağ I/O’sunu ciddi biçimde azaltır ve eğik anahtar dağılımlarında join’leri hızlandırır.
Spark’ta JVM çöp toplamayı ayarlamak için en iyi uygulamalar nelerdir?
ÇK duraklamalarını Spark UI veya VisualVM gibi araçlarla izleyin ve düşük duraklama süreleri için G1GC toplayıcısını tercih edin. Yürütücü belleğini ek yüke pay bırakacak şekilde ayırın (spark.executor.memoryOverhead) ve uzun dünya-duruyor duraklamalarını önlemek için -XX:InitiatingHeapOccupancyPercent değerini daha erken ÇK tetikleyecek şekilde ayarlayın.
Spark işlerini hızlandırmak için GPU hızlandırmasından nasıl yararlanabilirim?
Apache Spark için NVIDIA RAPIDS Accelerator’ı kullanarak SQL ve DataFrame işlemlerini GPU’lara şeffaf biçimde devredebilirsiniz. Bu hızlandırıcı, Spark’ın yürütme motoruna takılarak CPU tabanlı operatörlerin yerine GPU hızlandırmalı eşdeğerlerini koyar ve uygun iş yüklerinde 10 kata kadar hız sağlar.
Spark’ta statik ve dinamik kaynak tahsisi arasındaki fark nedir?
Statik tahsis, işin ömrü boyunca yürütücü sayısını sabitler; bu da öngörülebilirlik sağlar ancak olası boşta kaynak maliyeti yaratır. Dinamik tahsis ise bekleyen görevler ve iş yüküne göre yürütücü talep eder veya serbest bırakır; dalgalanan işler için küme kullanımını iyileştirir—paylaşılan ortamlara idealdir.
S3 gibi bulut depolama sistemlerinde en iyi performans için Spark’ı nasıl yapılandırmalıyım?
S3 aktarım hızlandırmasını etkinleştirin, spark.hadoop.fs.s3a.connection.maximum değerini ayarlayın ve tutarsızlıkla başa çıkmak için tutarlı görünümü (S3A v2) kullanın. Yazmadan önce küçük dosyaları birleştirin ve listeleme işlemi ek yükünü azaltıp yazma bant genişliğini artırmak için S3A committer’larını değerlendirin.
Kerberos ve TLS ile Spark iletişimini nasıl güvenceye alabilirim?
RPC için TLS’i etkinleştirin (spark.ssl.enabled) ve karşılıklı kimlik doğrulama için SASL/Kerberos’u yapılandırın (spark.authenticate and spark.kerberos.keytab). Kimlik bilgilerini güvenli, HDFS erişimli bir keytab’te saklayın ve yetkisiz veri ifşasını önlemek için ACL ayarlarıyla Spark UI erişimini kısıtlayın.
Pandas UDF’ler nedir ve ne zaman normal UDF’lerden daha verimlidir?
Pandas UDF’ler (vektörleştirilmiş UDF’ler), JVM ile Python arasında veriyi Apache Arrow ile toplu olarak değiş tokuş ederek serileştirme ek yükünü büyük ölçüde azaltır. Özellikle PySpark’ta büyük kolonar paketleri işlerken, karmaşık sayısal işlemlerde geleneksel satır-satır UDF’lerden daha iyi performans gösterirler.
Özel veri kaynakları için DataSource V2 API’si, V1’e göre ne gibi avantajlar sağlar?
DataSource V2, öngörü itme, bölüm budama ve akış kaynaklarını yerel olarak destekleyen daha temiz ve modüler bir arayüz sunar. İnce taneli okuma/yazma kontrolü ve Spark’ın Catalyst optimize edicisiyle daha iyi entegrasyon sağlar; sonuçta özel bağdaştırıcılar için daha yüksek performans ve daha kolay sürdürülebilirlik elde edilir.
Elektrik Mühendisliği, makine öğrenimi ve programlama alanlarında sağlam bir temele sahip bir Bulut Mühendisiyim. Kariyerime görüntü sınıflandırmaya odaklanan bilgisayarla görü alanında başladım, ardından MLOps ve DataOps’a geçiş yaptım. MLOps platformları kurma, veri bilimcilerini destekleme ve makine öğrenimi iş akışlarını kolaylaştırmak için Kubernetes tabanlı çözümler sunma konularında uzmanım.

