Yazılarımız

Veri Akademi

PL/SQL BULK COLLECT VE FORALL: BÜYÜK VERİ İŞLEMEDE PERFORMANS KAZANIMLARI

Büyük veri setleriyle çalışan PL/SQL kodlarında performansı belirleyen en kritik faktörlerden biri, SQL motoru ile PL/SQL motoru arasındaki geçişlerin sayısıdır. Kayıtları tek tek çekip tek tek güncelleyen döngüler ilk bakışta anlaşılır görünse de üretim ortamında beklenmedik gecikmelere, yoğun kilitlenmelere ve gereksiz CPU tüketimine yol açabilir. Tam da bu noktada Bulk Collect ve FORALL birlikte kullanıldığında, aynı işi daha az çağrı ile yaparak belirgin hız kazanımları sağlar.

Bu makalede, toplu veri okuma ve toplu DML yaklaşımının arkasındaki mantığı, gerçekçi örneklerle adım adım ele alacağız. Ayrıca LIMIT ile bellek kontrolü, SAVE EXCEPTIONS ile hata toleransı, ölçüm yöntemleri ve sık yapılan hatalar gibi pratik detaylara da değineceğiz.

Eğer sisteminizde yoğun ETL, batch job, toplu güncelleme veya raporlama öncesi hazırlık süreçleri varsa; doğru kurgulanmış bir Bulk Collect + FORALL mimarisi, aynı donanım üzerinde daha fazla iş yükünü taşımanıza yardım edebilir.


Primary keyword: PL/SQL Bulk Collect ve FORALL ile performans

SQL-PL/SQL context switch neden pahalıdır?

PL/SQL içinde her SQL çalıştırdığınızda, kontrol PL/SQL motorundan SQL motoruna geçer ve geri döner. Satır satır işlem yapan kodlarda bu geçiş yüzbinlerce kez tekrarlanabilir. Bu da, özellikle ağ gecikmesi olmasa bile, CPU üzerinde ciddi bir yük oluşturur. Bulk Collect bu geçiş sayısını okuma tarafında, FORALL ise yazma tarafında azaltmayı hedefler.

Row-by-row yaklaşımı hangi senaryolarda patlar?

Klasik “slow-by-slow” denilen desen, küçük tablolar veya nadir çalışan işlemler için kabul edilebilir olabilir. Ancak gün içinde sık çalışan job’lar, günlük milyonlarca kayıt üreten sistemler ya da indeksli büyük tablolar üzerinde güncelleme yapan süreçler, satır bazlı döngülerle kolayca darboğaza girer. Üstelik tek tek DML çalıştırmak, redo/undo ve kilit yönetimi açısından da maliyeti büyütür.

Koleksiyonlarla çalışan bir PL/SQL ekranında toplu okuma ve toplu yazma akışının özetlenmesi

Bulk Collect: Veriyi toplu okumak

Bulk Collect ile koleksiyonlara fetch mantığı

Bulk Collect, bir sorgunun sonucunu tek seferde veya parçalar halinde bir PL/SQL koleksiyonuna doldurur. Böylece her satır için ayrı fetch yerine, daha az sayıda fetch çağrısı yapılır. Bu yaklaşım özellikle “seç, dönüştür, yaz” tipindeki batch süreçlerinde fark yaratır.

Koleksiyon tipleri ve pratik seçim

Çoğu senaryoda associative array veya nested table yeterlidir. Dış dünyaya döndürülecek verilerde nested table tercih edilirken, internal işleme ve index bazlı erişimde associative array daha esnektir. Tip tanımlarını mümkün olduğunca tablo sütunlarına %TYPE ile bağlamak, şema değişikliklerine karşı dayanıklılığı artırır.

Örnek: LIMIT ile kontrollü toplu okuma

Aşağıdaki örnekte, sipariş satırlarını parti parti okuyup işliyoruz. LIMIT, bellek kullanımını kontrol eder; özellikle milyonlarca satırda tek seferde bulk collect yapmak yerine daha güvenli bir yöntem sunar.

DECLARE
  CURSOR c_lines IS
    SELECT order_id, line_id, amount
    FROM   order_lines
    WHERE  status = 'PENDING'
    ORDER  BY order_id;

  TYPE t_order_id IS TABLE OF order_lines.order_id%TYPE INDEX BY PLS_INTEGER;
  TYPE t_line_id  IS TABLE OF order_lines.line_id%TYPE  INDEX BY PLS_INTEGER;
  TYPE t_amount   IS TABLE OF order_lines.amount%TYPE   INDEX BY PLS_INTEGER;

  v_order_id t_order_id;
  v_line_id  t_line_id;
  v_amount   t_amount;

  v_limit PLS_INTEGER := 5000;
BEGIN
  OPEN c_lines;
  LOOP
    FETCH c_lines BULK COLLECT INTO v_order_id, v_line_id, v_amount LIMIT v_limit;
    EXIT WHEN v_order_id.COUNT = 0;

    -- burada dönüşüm/validasyon gibi PL/SQL işleri yapılabilir
    DBMS_OUTPUT.PUT_LINE('Batch size: ' || v_order_id.COUNT);
  END LOOP;
  CLOSE c_lines;
END;

FORALL: Toplu DML ile yazma maliyetini düşürmek

FORALL ile tek tek DML yerine toplu gönderim

FORALL, bir koleksiyonun indeks aralığı üzerinden DML komutunu toplu şekilde çalıştırır. Buradaki kritik nokta, PL/SQL döngüsü içinde “her iterasyonda ayrı INSERT/UPDATE” yapmak yerine, tek bir FORALL bloğunun SQL motoruna daha optimize bir biçimde iletilmesidir. Bu sayede hem context switch azalır hem de DML throughput artar.

Örnek: Bulk Collect + FORALL ile toplu güncelleme

Bu örnekte, bekleyen satırları okuyor, basit bir iş kuralı uyguluyor ve sonucu toplu güncelliyoruz. İş kuralı kısmı PL/SQL’de, yazma işlemi ise FORALL ile yapılır.

DECLARE
  CURSOR c_lines IS
    SELECT line_id, amount
    FROM   order_lines
    WHERE  status = 'PENDING';

  TYPE t_line_id IS TABLE OF order_lines.line_id%TYPE INDEX BY PLS_INTEGER;
  TYPE t_amount  IS TABLE OF order_lines.amount%TYPE  INDEX BY PLS_INTEGER;

  v_line_id t_line_id;
  v_amount  t_amount;

  v_limit PLS_INTEGER := 4000;
BEGIN
  OPEN c_lines;
  LOOP
    FETCH c_lines BULK COLLECT INTO v_line_id, v_amount LIMIT v_limit;
    EXIT WHEN v_line_id.COUNT = 0;

    -- İş kuralı: tutarı sınırla (örnek amaçlı)
    FOR i IN 1 .. v_amount.COUNT LOOP
      IF v_amount(i) < 0 THEN
        v_amount(i) := 0;
      END IF;
    END LOOP;

    FORALL i IN 1 .. v_line_id.COUNT
      UPDATE order_lines
      SET    amount = v_amount(i),
             status = 'PROCESSED',
             processed_at = SYSTIMESTAMP
      WHERE  line_id = v_line_id(i);
  END LOOP;
  CLOSE c_lines;
END;
LIMIT ile parça parça okunan kayıtların FORALL ile güncellenmesini anlatan iş akışı yaklaşımı

Hata yönetimi: SAVE EXCEPTIONS ve SQL%BULK_EXCEPTIONS

Toplu DML’de “ya hep ya hiç” yerine tolerans

FORALL içinde bir satır hataya düşerse, varsayılan davranış tüm işlemi durdurmaktır. Bazı batch süreçlerinde bu istenmez; hatalı satırlar loglanıp diğer satırların devam etmesi tercih edilir. SAVE EXCEPTIONS burada devreye girer: Hataları toplayıp işlem bittikten sonra incelemenize izin verir.

Örnek desen: Hataları yakala, indeksleri raporla

Aşağıdaki yaklaşımda, FORALL sonrası SQL%BULK_EXCEPTIONS ile hatalı indeksler ve hata kodları alınabilir. Üretim senaryosunda bu bilgiler bir hata tablosuna yazılabilir. Bu sayede batch job kesintiye uğramadan ilerler ve sorunlu kayıtlar sonradan ele alınır.

İpucu: SAVE EXCEPTIONS kullanıldığında beklenen hata oranı yüksekse, log yazma maliyeti de artar; log stratejinizi buna göre planlayın.

Bellek ve ölçek: LIMIT ayarı nasıl seçilir?

LIMIT seçimi: performans ile güvenlik dengesi

LIMIT çok düşük olursa daha fazla fetch döngüsü çalışır ve kazanım azalır; çok yüksek olursa bellek tüketimi artar, PGA baskısı oluşabilir. En doğru değer; satır genişliği, dönüşüm maliyeti, sistemin eşzamanlı job sayısı ve bakım penceresi gibi faktörlere bağlıdır. Genelde birkaç bin ile birkaç on bin arası değerler pratikte iyi sonuç verir; ancak bunu mutlaka ölçerek doğrulamak gerekir.

Bulk Collect tek seferde mi, parçalı mı?

Veri seti küçükse tek seferde toplamak kodu basitleştirir. Ancak “her gece milyonlarca satır” gibi bir gerçeklik varsa, parçalı yaklaşım daha güvenlidir. Özellikle dönüşüm sırasında ek koleksiyonlar veya lookup’lar kullanıyorsanız, bellek tepe noktası beklenenden hızlı büyüyebilir. Bu durumda LIMIT hem istikrar hem de öngörülebilirlik sağlar.

Performans ölçümü: Kazancı nasıl kanıtlarsınız?

Basit ölçüm: süre, satır sayısı, throughput

İyileştirmeyi göstermek için en anlaşılır metrikler; toplam süre, işlenen satır sayısı ve saniye başına satır (throughput) değeridir. Tek bir test koşuluna güvenmek yerine, farklı hacimlerde ve farklı saatlerde tekrar etmek daha sağlıklı sonuç verir. Ayrıca commit stratejisi (ör. her batch sonunda commit) ve indeks/trigger etkileri testte sabitlenmelidir.

En sık görülen performans tuzakları

  • FORALL yerine normal FOR döngüsü içinde DML çalıştırmak
  • LIMIT kullanmadan çok büyük koleksiyonları doldurmak
  • FORALL içinde gereksiz dönüşümler ve fonksiyon çağrıları yapmak
  • Yanlış commit sıklığıyla undo/redo baskısı yaratmak
  • Hata loglamasını aşırı ayrıntılı yapıp IO darboğazı oluşturmak

Bu tuzaklardan kaçınmak için, kodun hedefinin “daha az SQL çağrısı” olduğunu akılda tutun. Dönüşüm tarafında mantığı sadeleştirmek, yazma tarafında ise toplu DML’yi korumak genellikle iyi bir başlangıçtır.

Okunabilirlik ve bakım: Kod nasıl sade kalır?

İş kuralını ayırın, DML’yi merkezileştirin

Bulk Collect + FORALL kullanan kodlar bazen “karmaşık” etiketini haksız yere alır. Aslında iş kuralı kısmını ayrı prosedürlere bölmek, koleksiyon tiplerini paket içinde tanımlamak ve DML kısmını tek bir FORALL bloğunda toplamak, hem okunabilirliği hem de test edilebilirliği artırır. Böylece performans kazanımı sağlarken bakım maliyetini büyütmemiş olursunuz.

Doğru eğitim ve pratikle hızlanın

Bu konular, özellikle üretim sistemlerinde doğru kullanıldığında çok değerli kazanımlar sağlar. İleri seviye örnekler, hata yönetimi ve ölçüm teknikleriyle daha hızlı ilerlemek isterseniz Oracle PL/SQL eğitimi içeriğine göz atabilirsiniz.

Hatalı satırların ayrıştırılıp kalan kayıtların işlenmeye devam ettiği toplu işlem yaklaşımı

Ne zaman Bulk Collect ve FORALL kullanmamalısınız?

Az veri, yüksek eşzamanlılık veya OLTP kritik yollar

Her problem “bulk” ile çözülmez. Çok küçük veri setlerinde kazanç ihmal edilebilir; hatta gereksiz karmaşıklık yaratabilir. OLTP tarafında kullanıcı etkileşimli akışlarda, büyük koleksiyonlar tutmak yerine daha hedefli SQL yazmak daha doğru olabilir. Ayrıca çok yüksek eşzamanlılıkta, aynı kaynaklara toplu DML ile yüklenmek kilit beklemelerini artırabilir.

Alternatif yaklaşım: Set-based SQL ve MERGE

Birçok batch işinde en hızlı yol, PL/SQL’e fazla iş bırakmadan set-based SQL kullanmaktır. MERGE, INSERT SELECT, UPDATE JOIN gibi yöntemler, doğru indeks ve istatistiklerle son derece verimli olabilir. Bulk Collect + FORALL genellikle “PL/SQL içinde iş kuralı uygulamak zorundayım” dediğiniz noktada en anlamlı hale gelir.

Özet: Sağlam bir toplu işleme stratejisi

Bulk Collect okuma tarafında, FORALL yazma tarafında context switch maliyetini azaltarak büyük veri işlemede belirgin hız kazandırır. LIMIT ile bellek kontrolünü sağlamak, SAVE EXCEPTIONS ile hataya dayanıklılığı artırmak ve ölçümle doğrulamak, kalıcı bir performans iyileştirmesi için temel adımlardır. Doğru senaryoda uygulandığında, aynı iş yükünü daha kısa bakım penceresinde tamamlamak mümkün hale gelir.

 VERİ AKADEMİ