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:
- Partition’suz bir tablonun tamamını chunk’lara bölerek işleme
- Bir tablonun tarih kolonuna göre belirli bir aralığı chunk’lara bölerek işleme
- Partition’lı bir tablonun sadece belirtilen partition’larını, her partition’ı kendi içinde chunk’lara ayırarak işleme
- 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 tabloTARGET_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