Oracle’da Büyük Tablolarda Chunk Bazlı Paralel Veri İşleme

YUNUS EMRE ATAY

Oracle DBMS_PARALLEL_EXECUTE

Oracle veritabanlarında büyük hacimli tablolar üzerinde yapılan veri taşıma, arşivleme veya batch DML işlemleri çoğu zaman performans problemlerine yol açar. Milyonlarca satırı tek seferde işlemek; uzun süren transaction’lara, yoğun undo/redo kullanımına ve sistem kaynaklarının zorlanmasına neden olur.

Bu nedenle veriyi küçük parçalara (chunk) bölerek, kontrollü ve mümkünse paralel şekilde işlemek en sağlıklı yaklaşımdır.

⚠️ Ancak kritik bir nokta vardır:
Bu yöntemlerin tamamı, tabloya sürekli veri gelmediği (ya da kontrollü geldiği) senaryolar için tasarlanmıştır.
Bu bilgi çoğu zaman atlanır ve ciddi veri tutarsızlıklarına yol açabilir.

Bu yazıda aşağıdaki dört yaklaşımı ele alacağız:

  1. Partition’suz bir tablonun tamamını chunk’lara bölerek işleme
  2. Bir tablonun tarih kolonuna göre belirli bir aralığı chunk’lara bölerek işleme
  3. Partition’lı bir tablonun sadece belirtilen partition’larını, her partition’ı kendi içinde chunk’lara ayırarak işleme
  4. DBMS_PARALLEL_EXECUTE kullanmadan, DBA_EXTENTS üzerinden manuel chunk üretme


Önemli Ön Bilgi: Sürekli Veri Gelen Tablolarda Bu Yöntemler Çalışır mı?

Bu sorunun cevabı nettir:

Hayır, bu yöntemler sürekli aktif DML olan tablolarda doğrudan güvenli değildir.

Neden?

  • Chunk’lar belirli bir anın fotoğrafına göre oluşturulur
  • Sonradan gelen satırlar:
    • Bazı chunk’lara hiç girmeyebilir
    • Ya da aynı satır birden fazla chunk tarafından işlenebilir
  • ROWID ve BLOCK bazlı işlemler dinamik veri değişiminden etkilenir

Güvenli Kullanım Senaryoları

Bu yöntemler şu durumlarda güvenlidir:

  • ETL / batch penceresi vardır
  • Kaynak tabloya veri akışı durdurulmuştur
  • Ya da sadece geçmiş tarihli (immutable) veriler işlenmektedir
  • Partition’lar READ ONLY ya da artık veri almıyordur

👉 Eğer tablo gün boyunca insert/update alıyorsa, mutlaka:

  • SCN bazlı filtre
  • Snapshot tablo
  • Ya da staging yaklaşımı kullanılmalıdır

DBMS_PARALLEL_EXECUTE Nedir?

DBMS_PARALLEL_EXECUTE, Oracle 11g ve sonrası sürümlerde bulunan ve büyük DML işlemlerini küçük ve bağımsız parçalara bölerek paralel çalıştırmayı sağlayan bir PL/SQL paketidir.

Başlıca avantajları:

  • Büyük tabloları yönetilebilir parçalara ayırır
  • Paralel çalışarak işlem süresini ciddi şekilde kısaltır
  • Hata alan chunk’ları tekrar çalıştırma imkanı sunar
  • Uzun süren işlemlerde kontrol ve esneklik sağlar


Senaryo ve Genel Yaklaşım

Örneklerde aşağıdaki tabloları kullanalım:

  • SOURCE_TABLE → Büyük veri içeren kaynak tablo
  • TARGET_TABLE → Verinin insert edileceği hedef tablo

Amaç:
Veriyi küçük chunk’lar halinde, paralel, kontrollü bir şekilde taşımak.


1️⃣ Partition’suz Bir Tablonun Tamamını Chunk’lara Bölerek Paralel İşleme

Ne İş Yapar?

Bu adım:

  • Partition’suz bir tablonun tamamını
  • Fiziksel ROWID aralıklarına bölerek
  • Küçük chunk’lar halinde işlemeye hazırlar

⚠️ Tabloya bu sırada insert/update gelmemelidir.

Task Oluşturma:
BEGIN
  DBMS_PARALLEL_EXECUTE.create_task(
    task_name => 'DATA_TRANSFER_TASK'
  );
END;
/

Chunk Oluşturma (ROWID Bazlı):

BEGIN
  DBMS_PARALLEL_EXECUTE.create_chunks_by_rowid(
    task_name   => 'DATA_TRANSFER_TASK',
    table_owner => 'SCHEMA_NAME',
    table_name  => 'SOURCE_TABLE',
    by_row      => TRUE,
    chunk_size  => 10000
  );
END;
/

Paralel Insert Çalıştırma:

BEGIN
  DBMS_PARALLEL_EXECUTE.run_task(
    task_name      => 'DATA_TRANSFER_TASK',
    sql_stmt       => '
      INSERT INTO TARGET_TABLE
      SELECT *
      FROM SOURCE_TABLE
      WHERE ROWID BETWEEN :start_id AND :end_id
    ',
    language_flag  => DBMS_SQL.NATIVE,
    parallel_level => 8
  );
END;
/


2️⃣ Bir Tablonun Tarih Kolonuna Göre Belirtilen Aralığı Chunk’lara Bölerek İşleme

Ne İş Yapar?

Bu yöntem:

  • Tablonun tamamını değil
  • Belirli bir tarih aralığını
  • Günlük (veya istenen periyotta) chunk’lara böler

👉 Sürekli veri gelen tablolarda EN GÜVENLİ yaklaşımdır,
çünkü sadece geçmiş (değişmeyen) veriyi işler.

Tarih Bazlı Chunk Oluşturma:

BEGIN
  DBMS_PARALLEL_EXECUTE.create_chunks_by_sql(
    task_name => 'DATA_TRANSFER_TASK',
    sql_stmt  => '
      SELECT
        MIN(ROWID) AS start_id,
        MAX(ROWID) AS end_id
      FROM SOURCE_TABLE
      WHERE create_date BETWEEN
            DATE ''2024-01-01'' AND DATE ''2024-01-31''
      GROUP BY TRUNC(create_date)
    '
  );
END;
/


3️⃣ Partition’lı Bir Tabloda Belirtilen Partition’ları Chunk’lara Bölerek İşleme

Ne İş Yapar?

Bu yaklaşım:

  • Sadece belirtilen partition’ları
  • Her partition’ı kendi içinde küçük chunk’lara böler

⚠️ Bu yöntem yalnızca artık veri almayan partition’lar için güvenlidir.

Partition Bazlı Chunk Oluşturma:

BEGIN
  DBMS_PARALLEL_EXECUTE.create_chunks_by_sql(
    task_name => 'DATA_TRANSFER_TASK',
    sql_stmt  => '
      SELECT
        MIN(ROWID) AS start_id,
        MAX(ROWID) AS end_id
      FROM SOURCE_TABLE PARTITION (P_202401)
      GROUP BY TRUNC(create_date)
    '
  );
END;
/


4️⃣ DBMS_PARALLEL_EXECUTE Kullanmadan Manuel Chunk Üretme (DBA_EXTENTS)

Ne İş Yapar?

Bu yöntem:

  • DBMS_PARALLEL_EXECUTE kullanmadan
  • Fiziksel block seviyesinde
  • Veriyi manuel olarak parçalara ayırır

⚠️ Bu yöntem aktif DML olan tablolarda KESİNLİKLE kullanılmamalıdır.

Mantık

  • Verinin fiziksel yerleşimi DBA_EXTENTS’ten alınır
  • Block’lar NTILE(N) ile N parçaya bölünür
  • Her chunk için:
    • Başlangıç ve bitiş file#
    • Başlangıç ve bitiş block_id hesaplanır

Manuel Chunk Üreten SQL:

WITH extents AS (
  SELECT
    file_id,
    block_id,
    blocks
  FROM dba_extents
  WHERE owner          = 'SCHEMA_NAME'
    AND segment_name   = 'SOURCE_TABLE'
    AND partition_name = 'P_202401'
),
expanded_blocks AS (
  SELECT
    file_id,
    block_id + LEVEL - 1 AS block_no
  FROM extents
  CONNECT BY LEVEL <= blocks
         AND PRIOR file_id = file_id
         AND PRIOR block_id = block_id
         AND PRIOR SYS_GUID() IS NOT NULL
),
grouped AS (
  SELECT
    NTILE(100) OVER (ORDER BY file_id, block_no) AS group_no,
    file_id,
    block_no
  FROM expanded_blocks
)
SELECT
  group_no,
  MIN(file_id)  AS lo_fno,
  MAX(file_id)  AS hi_fno,
  MIN(block_no) AS lo_block,
  MAX(block_no) AS hi_block,
  COUNT(*)      AS sum_blocks
FROM grouped
GROUP BY group_no
ORDER BY group_no;

Bu Chunk’larla Insert İşlemi:

INSERT INTO target_table
SELECT *
FROM source_table
WHERE DBMS_ROWID.ROWID_RELATIVE_FNO(rowid) = :lo_fno
  AND DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid)
      BETWEEN :lo_block AND :hi_block;

INSERT Komutlarını Üreten SQL:

WITH extents AS (
  SELECT
    file_id,
    block_id,
    blocks
  FROM dba_extents
  WHERE owner          = 'SCHEMA_NAME'
    AND segment_name   = 'SOURCE_TABLE'
    AND partition_name = 'P_202401'
),
expanded_blocks AS (
  SELECT
    file_id,
    block_id + LEVEL - 1 AS block_no
  FROM extents
  CONNECT BY LEVEL <= blocks
         AND PRIOR file_id = file_id
         AND PRIOR block_id = block_id
         AND PRIOR SYS_GUID() IS NOT NULL
),
grouped AS (
  SELECT
    NTILE(100) OVER (ORDER BY file_id, block_no) AS group_no,
    file_id,
    block_no
  FROM expanded_blocks
),
ranges AS (
  SELECT
    group_no,
    MIN(file_id)  AS lo_fno,
    MAX(file_id)  AS hi_fno,
    MIN(block_no) AS lo_block,
    MAX(block_no) AS hi_block
  FROM grouped
  GROUP BY group_no
)
SELECT
  '/* CHUNK ' || group_no || ' */ ' ||
  'INSERT INTO TARGET_TABLE ' ||
  'SELECT * FROM SCHEMA_NAME.SOURCE_TABLE PARTIITON(P_202401) ' ||
  'WHERE DBMS_ROWID.ROWID_RELATIVE_FNO(rowid) = ' || lo_fno || ' ' ||
  'AND DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid) BETWEEN ' ||
  lo_block || ' AND ' || hi_block || ';'
  AS insert_sql
FROM ranges
ORDER BY group_no;

Sonuç

Chunk bazlı veri işleme, Oracle’da büyük tablolarla çalışırken vazgeçilmezdir.
Ancak bu teknikler verinin stabil olduğu varsayımıyla çalışır.

Leave a Reply to %s

Your email address will not be published. Required fields are marked with *

Hey!

Hello! I'm EMRE, and I'm passionate about technology, software, databases, and data analytics. In this blog, I aim to provide helpful content by sharing my learnings and experiences. In my spare time, I enjoy exploring new technologies, writing, and self-improvement.

My contact addresses