Her çekirdek işe yarasın: Uber’de Spark’ı Gluten/Velox ve storage-aware engine ile hızlandırmak
Making Every Core Count: In Pursuit Of Faster Apache Spark Jobs at Uber
Uber dünyanın en büyük Apache Spark filolarından birini işletiyor: yalnızca data lake ingestion 200.000’den fazla CPU çekirdeği tüketiyor, üzerindeki ETL daha da fazlasını. Spark altyapısı günde 100 PB’tan fazla veri okuyup yazıyor, 80 PB’tan fazla shuffle verisi taşıyor, günde 500.000’den fazla uygulama ve bir milyondan fazla çekirdek çalıştırıyor. Bu ölçekte yüzde birkaçlık verim bile gerçek etki; bu yüzden Spark’ı ucuzlatmak tek seferlik bir proje değil sürekli bir program. İşin büyük kısmı, her Spark uygulamasının nasıl gönderildiğini, yapılandırıldığını ve yayınlandığını kontrol eden Spark-as-a-Service platformu Drogon üzerinden yürüyor; bu da filo çapında engine değişikliklerini mümkün kılıyor.
Birinci adım: Gluten ve Velox. Apache Gluten, JVM tabanlı SQL engine’lerinin çalıştırılmasını Velox gibi native engine’lere devreden bir ara katman; native yürütme SIMD gibi yetenekleri açıyor. Uber’in bildirdiği sonuçlar: taşınan iş yüklerinde yaklaşık 3,5 kat daha hızlı sorgu ve 3,4 kat daha düşük CPU maliyeti, kullanıcı kodunda değişiklik olmadan ve ortalama yüzde 79’a varan runtime iyileşmesi. TPC-DS benchmark’ında 64 executor ile en iyi durumda 4 kat, 32 executor ile en kötü durumda 2,7 kat, genel ortalamada yaklaşık 3,4 kat iyileşme görülmüş. İlginç bir bulgu: native shuffle’da columnar-to-row dönüşümü ek yük yaratıyor ve shuffle ağırlıklı sorgularda Gluten geriye düşebiliyor (bir sorguda Spark 10 saniyede, Gluten 1 dakikada bitmiş). Celeborn’un columnar sort shuffle’ı ile bu dönüşüm ortadan kalkınca ortalama iyileşme yaklaşık 3,65 kata çıkmış. Production’da seçili iş yüklerinde runtime’da yüzde 79’a, kritik pipeline’larda maliyette yüzde 98’e kadar azalma görülmüş (örneğin bir iş 1 saat 7 dakikadan 16 dakikaya inmiş). Üretime hazırlık için Uber upstream’e 30’dan fazla yama (50’den fazla commit) göndermiş: row-to-column dönüşümünden doğan maliyet artışlarını azaltan fallback mekanizmaları ve bellek ayarları (Velox için off-heap, diğer işlemler için on-heap bellek ayrımı), native C++ Parquet okuyucu ve yazıcılarda şifreleme desteği ve eksik operatör ve fonksiyonlar.
İkinci adım: storage-aware engine değerlendirmesi. Gluten kazanımlarının üstüne Uber, Spark hesaplamasının en büyük payını oluşturan ingestion ve tablo bakımı iş akışlarına baktı: bunlar yalnızca SQL operatörleri değil, mevcut veriyi okuyup güncelleme uygulayan, dosya yazan ve tablo metadata’sını güncel tutan işler. Data lake’teki tabloların çoğu düz Parquet; yaklaşık 17.200 tablo Apache Hudi ile yönetiliyor ve Hudi iş yükleri ingestion hesaplamasının önemli bir kısmı. Uber, Onehouse’un ticari Spark engine’i Quanton’ı benchmark etti. Production iş yüklerinden modellenmiş sentetik veriyle, kontrollü bir ortamda: 60 milyar kayıtlı, insert ağırlıklı bir fact tablosunda 1.080 vCPU ile write-round başına medyan 5,5 kat hızlanma; 10 milyar kayıtlı, update ağırlıklı bir dimension tablosunda 3.300 vCPU ile 4,7 kat. Her karşılaştırmada Quanton ve açık kaynak Spark/Hudi baseline’ı aynı executor tahsisini kullanmış; sonuçlar metadata-compaction turlarını içermiyor. Bu rakamlar bir tedarikçinin ürünü, sentetik veri ve kontrollü ortam için; Uber production sonucu değil, bir değerlendirme olarak sunuyor.
Öne çıkanlar
- Bu ölçekte yüzde birkaçlık verim bile gerçek bir kazançtır; optimizasyon sürekli bir programdır.
- Platform katmanı (Drogon) tüm Spark uygulamalarının gönderimini kontrol ettiği için filo çapında engine değişikliği mümkündür.
- Native engine’e geçince shuffle ile row/column dönüşümü darboğaz olabilir; ortalama iyileşme tek tek sorgularda ters düşebilir.
- Vendor benchmark’ını sentetik veriyle ve aynı kaynakla yaptığın baseline ile karşılaştır; production sonucu olarak okuma.
Neden önemli?
Çoğu Spark optimizasyonu SQL operatörlerine odaklanır; ama yazının gösterdiği gibi ingestion ve tablo bakımı gibi okuma, güncelleme ve yazma yollarını içeren iş yükleri, hesaplamanın büyük kısmını yer. Optimizasyonu büyük harcama kalemine yönlendirmek, önce ölçmekle başlar.
Pratikte
Kendi Spark ya da Pandas işlerinde üç hızlı kontrol: (1) Shuffle’ı ölç; Spark UI’da ‘Shuffle Read/Write’ boyutuna bak, büyükse join stratejini (broadcast join) ya da partition sayısını gözden geçir. (2) Düz CSV yerine Parquet kullan ve gereksiz kolonları okuma (column pruning). (3) Bir performans iddiasını (‘5 kat hızlı’) kendi verinle, aynı kaynakla, birkaç kez tekrarlayarak ölç. Vendor sayıları yalnızca hipotez.
Sözlük
- shuffle
- Dağıtık işlemede verinin düğümler arasında yeniden dağıtılması; genelde en pahalı adımlardan biridir.
- vectorized execution
- Veriyi satır satır değil toplu bloklar hâlinde, SIMD gibi donanım yetenekleriyle işleme.
- Apache Hudi
- Data lake’te güncelleme ve silmeyi destekleyen açık kaynak tablo formatı.
- fallback
- Hızlı yol çalışmadığında sistemin yavaş ama güvenli yola düşmesi.
- column pruning
- Bir sorgunun yalnızca ihtiyaç duyduğu kolonları diskten okuması.