← Ana sayfaya dön

Yayın tarihi:

Kodunuzdaki Tek Bir Felsefe Değişikliği Performansı 100+ Kat Artırabilir Mi?

Saatlerce süren bir ETL işlemi, satır satır düşünmekten set tabanlı düşünmeye geçilerek 3,5 dakikaya indirildi — 100 kattan fazla performans artışının hikayesi.

Saatlerce süren ETL işlemleri, sürekli tıkanan veritabanları, ne zaman biteceği belirsiz bekleyişler... Tanıdık geliyor mu?

Geçenlerde karşılaştığımız 6+ saatlik bir işlemde tam olarak bunu yaşıyorduk.

Sorun: O Meşhur "Döngü İçinde Sorgu" Anti-Pattern'i

Kodun ilk hali, teoride masum görünen ama pratikte bir performans katili olan bir yapıya sahipti. Milyonlarca satırı işlemek için izlediği yol şuydu:

  1. Ana tablodan veriyi bir CURSOR ile al.
  2. Bir döngü (LOOP) başlat.
  3. Döngünün içinde, o anki tek bir satır için diğer tablolara tekrar tekrar SELECT sorguları atarak ek bilgileri bul.
  4. Yine döngünün içinde, bu tek satırı hedef tabloya INSERT et.
  5. Sonraki satıra geç ve üçüncü adımı milyonlarca kez tekrarla.

Sorun nerede mi? Milyonlarca satır, PL/SQL motoru ile SQL motoru arasında milyonlarca kez gidip gelmek demek. Bu, "context switch" adı verilen ve sistem kaynaklarını tüketen korkunç bir maliyet yaratır.

Peki ya çözümün, bu karmaşık döngüyü düzeltmek yerine, sadece o koda yaklaşımımızı değiştirmek olduğunu söylesem?

Bizim için her şeyi değiştiren "Aha!" anı buydu. O tek felsefe değişikliği ise şuydu:

Satır Satır Düşünmekten Vazgeçip, Set Tabanlı (Toplu) Düşünmeye Başlamak.

Yani, veritabanına komutları tek tek veren bir mikro-yönetici gibi değil, ne istediğini söyleyip gerisini ona bırakan bir stratejist gibi davranmak.

Bu felsefeyi koda döktüğümüzde ise ortaya çıkan tablo inanılmazdı. 6+ saatlik işlem 3,5 dakikada tamamlandı. Tam 100 kattan fazla bir performans artışı!

Peki Ama Nasıl? İşte Dokunuşlar

İşte bu felsefenin koda yansımış hali:

DECLARE
    -- 1. Tüm mantık, set tabanlı düşünen tek bir CURSOR'da.
    CURSOR c_kayitlar IS
    WITH ...
    SELECT
        /*+ PARALLEL(16) */ ...
    FROM
        Kaynak_Tablo KT ...;

    -- 2. Veri, yığınlar halinde tutulacak bir koleksiyonda.
    TYPE t_record_list IS TABLE OF c_kayitlar%ROWTYPE;
    v_KAYITLAR t_record_list;
    c_BATCH_SIZE CONSTANT PLS_INTEGER := 100000;
BEGIN
    OPEN c_kayitlar;
    LOOP
        -- 3. Veri toplu çekilir.
        FETCH c_kayitlar BULK COLLECT INTO v_KAYITLAR LIMIT c_BATCH_SIZE;
        EXIT WHEN v_KAYITLAR.Count = 0;

        -- 4. Veri toplu yazılır.
        FORALL i IN INDICES OF v_KAYITLAR
            INSERT /*+ APPEND PARALLEL(8) */ INTO Hedef_Tablo VALUES v_KAYITLAR(i);

        COMMIT;
    END LOOP;
    CLOSE c_kayitlar;
END;

Sonuçta anladık ki, en büyük performans kazancı, en karmaşık algoritmada değil, en temel yaklaşım değişikliğindeydi.

Bu yazı ilk olarak LinkedIn'de yayınlanmıştır.