---
title: 'Ölü Satır Temizliği'
source: 'https://academia.sh/tr/kurslar/veritabani-yonetimi/olu-satir-temizligi'
course: 'İlişkisel Veritabanı Yönetimi'
language: tr
updated: '2026-08-17T18:08:56+00:00'
license: 'CC BY-SA 4.0'
---

# Ölü Satır Temizliği

Silme ve güncellemenin geride bıraktığı ölü sürümler, silinen yerin dosyadan geri gelmemesi, boşluğun neden her zaman yeniden kullanılamadığı, tam yeniden yazmanın maliyeti ve şişme oranının temizlik sıklığı ile açık işlem süresine bağlılığı.

Önceki ders, bir sürümün ancak hiçbir açık anlık görüntü tarafından görülmediğinde
kaldırılabilir hâle geldiğini gösterdi. Kaldırılabilir olmak ile kaldırılmış olmak ayrı
şeylerdir. Sürümleri toplayan ayrı bir süreç vardır ve bu süreç, üretilen ölü sürümlere
yetişemezse tablo, satır sayısı hiç artmasa bile büyümeye devam eder.

Bu dersin soruları ölçülebilir: silinen satırların yeri nereye gider, o yer ne zaman
yeniden kullanılır, kullanılmadığında dosya ne kadar büyür ve bu büyümeyi geri almanın
bedeli nedir?

## Ölü Satır ve Şişme

**Ölü satır (dead tuple)**, artık hiçbir işlemin görmediği bir satır sürümüdür. İki
kaynaktan doğar: silinen satırın kendisi ve güncellenen satırın eski sürümü. Önceki dersteki
model gereği güncelleme de bir ölü sürüm bırakır, çünkü fiziksel olarak yeni bir sürüm
yazılır.

**Şişme (bloat)**, bir tablonun ya da dizinin kapladığı yerin, içindeki canlı veriyi tutmak
için gereken yerden ne kadar büyük olduğudur. Ölçüsü bir orandır: kaplanan sayfa sayısının,
aynı verinin sıkıştırılmış hâlde kaplayacağı sayfa sayısına bölümü.

Şişme kendi başına bir hata değildir; bir miktarı kaçınılmazdır ve yararlıdır, çünkü boşluk
yeni satırlar için yer bırakır. Sorun, oranın büyümeyi sürdürmesidir. Şişmiş bir tablonun
taranması daha çok sayfa okur, tampon havuzunda daha çok yer tutar ve yedekleri büyütür —
hepsi aynı veri için.

## Silinen Yer Nereye Gider

Aşağıdaki koşum dört aşamayı aynı üç ölçüyle izler: satır sayısı, sayfa sayısı ve sayfa
içi doluluk oranı. Dosya boyutu her aşamada ayrıca yazdırılır.

```sh
rm -f s.db
cat > durum.sql <<'EOF'
SELECT (SELECT count(*) FROM odunc) AS satir,
       (SELECT page_count FROM pragma_page_count) AS sayfa,
       (SELECT round(100.0*sum(payload)/sum(pgsize),1) FROM dbstat WHERE name='odunc') AS doluluk;
EOF
sqlite3 s.db <<'SQL'
.mode column
.headers on
CREATE TABLE odunc(odunc_id INTEGER PRIMARY KEY, kitap_id INT, uye_id INT,
                   alis_tarihi TEXT, iade_tarihi TEXT);
INSERT INTO odunc SELECT n, 1+(n*7)%200000, 1+(n*13)%120000,
       date('2018-01-01','+'||((n*37)%2437)||' days'), NULL
  FROM (WITH RECURSIVE s(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM s WHERE n<400000) SELECT n FROM s);
.print '--- baslangic ---'
.read durum.sql
.shell echo "dosya: $(wc -c < s.db)"
DELETE FROM odunc WHERE alis_tarihi < '2020-01-01';
.print '--- 119822 satir silindi ---'
.read durum.sql
.shell echo "dosya: $(wc -c < s.db)"
INSERT INTO odunc SELECT 500000+n, 1+(n*7)%200000, 1+(n*13)%120000, '2025-01-01', NULL
  FROM (WITH RECURSIVE s(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM s WHERE n<100000) SELECT n FROM s);
.print '--- 100000 yeni satir (artan anahtarla) ---'
.read durum.sql
.shell echo "dosya: $(wc -c < s.db)"
VACUUM;
.print '--- VACUUM sonrasi ---'
.read durum.sql
.shell echo "dosya: $(wc -c < s.db)"
SQL
```

```
--- baslangic ---
satir   sayfa  doluluk
------  -----  -------
400000  2723   77.3   
dosya:  11153408
--- 119822 satir silindi ---
satir   sayfa  doluluk
------  -----  -------
280178  2723   54.2   
dosya:  11153408
--- 100000 yeni satir (artan anahtarla) ---
satir   sayfa  doluluk
------  -----  -------
380178  3403   58.8   
dosya:  13938688
--- VACUUM sonrasi ---
satir   sayfa  doluluk
------  -----  -------
380178  2589   77.3   
dosya:  10604544
```

İkinci aşama ilk sonucu veriyor. Satırların yaklaşık üçte biri silindi; sayfa sayısı bir
tane bile azalmadı, dosya boyutu bir bayt bile küçülmedi. Değişen tek şey doluluk: %77,3'ten
%54,2'ye düştü. Silme, sayfaları serbest bırakmadı — sayfaların **içini** boşalttı.

Bu davranışın nedeni doğrudandır. Silinen satırlar dosyanın her yerine dağılmıştır; bir
sayfanın tamamen boşalması ancak o sayfadaki bütün satırların silinmesiyle olur. Kısmen
boşalmış bir sayfa dosyadan çıkarılamaz, çünkü Fiziksel Depolama Düzeni dersinde ölçülen
ilişki gereği dosya, sayfaların art arda dizilmesinden ibarettir; ortadaki bir sayfayı
çıkarmak sonrasındaki her şeyin yerini değiştirmek demektir.

## Boşluk Neden Her Zaman Yeniden Kullanılmaz

Üçüncü aşama daha ilginç bir sonuç veriyor. Tabloda 119.822 satırlık boşluk varken 100.000
satır eklendi ve dosya küçülmek bir yana **büyüdü**: 2.723 sayfadan 3.403 sayfaya.

Nedeni anahtar seçimidir. `odunc_id` sürekli artan bir değerdir ve yeni satırlar ağacın en
sağ ucuna eklenir. Silinen satırların bıraktığı boşluklar ise ağacın ortalarındadır — yeni
anahtarlar oraya ait değildir. Motor, sıralı bir yapıda bir anahtarı ait olmadığı yere
koyamaz.

Bu, şema tasarımıyla depolama arasındaki en somut bağlardan biridir. Sürekli artan bir
anahtarla eklenen ve eski kayıtları tarihe göre silinen bir tabloda boşluk kendiliğinden
kapanmaz: yeni veri hep sona yazılır, boşluk hep ortada kalır. Aynı tablo rastgele
anahtarlarla doldurulsaydı boşlukların bir kısmı doğal olarak kullanılırdı — bu kez de
sayfa bölünmeleri artardı. Bölümleme dersi bu soruna yapısal bir cevap getirecek: eski veriyi
silmek yerine, eski bölümün tamamını kaldırmak.

## Boşluğun Geri Kazanılması

Dördüncü aşama `VACUUM` çalıştırıyor ve tablo 3.403 sayfadan 2.589 sayfaya iniyor; doluluk
%77,3'e, yani ilk günkü değerine dönüyor. Şişme oranı 3403 / 2589 ≈ 1,31 idi; yeniden yazma
onu 1,00'e indirdi.

Bu işlemin ne yaptığını bilmek, ne zaman kullanılacağını bilmekten önce gelir: tablo baştan
sona okunur ve yeni bir yere sıkıştırılmış olarak yazılır, sonra eskisinin yeri bırakılır.
Üç bedeli vardır.

**Kilit.** Tam yeniden yazma sırasında tablo (çoğu motorda) değiştirilemez, bazılarında hiç
okunamaz. Çalışan bir sistemde bu, planlanmış bir kesinti demektir.

**Geçici alan.** Yeni kopya yazılırken eski kopya hâlâ yerindedir; işlem sırasında tablonun
boyutu kadar ek yer gerekir. Diski dolmak üzere olan bir sunucuda yer açmak için yeniden
yazma çalıştırmak, elde olmayan bir alanı gerektirdiği için işe yaramaz.

**Süre.** Maliyet tablonun tamamıyla orantılıdır, geri kazanılacak boşlukla değil.

Bu yüzden motorlar iki hafifletilmiş yol sunar: boşluğu kilitlemeden yeniden kullanılabilir
hâle getiren arka plan temizliği ve tabloyu çalışır durumda tutarak yeni bir kopya kuran
yeniden düzenleme. Adları ve sınırları motora özgüdür; tam yeniden yazma, kural olarak son
çaredir.

## Temizliğin Dengesi

Asıl yönetim sorusu "ne zaman yeniden yazmalı" değil, "şişmenin belli bir oranın üstüne
çıkmasını ne engelliyor" sorusudur. Aşağıdaki model iki değişkeni ayrı ayrı değiştirir:
temizliğin çalışma sıklığı ve açık bir işlemin süresi. Gerçek bir temizlik sürecinin
kendisi değildir; ölçüde tuttuğu şey, ayrılan en yüksek yerin canlı veriye oranıdır.

```sh
cat > temizlik.mjs <<'JS'
// Model: olu surumlerin birikmesi ile temizligin dengesi. Gercek motorun kendisi degil.
// Her adimda GUNCELLEME satir guncellenir; her guncelleme bir olu surum birakir.
// Temizlik ARALIK adimda bir calisir ve yalnizca en eski acik anlik goruntuden
// daha eski olu surumleri toplar. Ayrilan yer isletim sistemine geri verilmez.
function calistir({ aralik, uzunIslem = null, adim = 600 }) {
  const CANLI = 100000, GUNCELLEME = 2000;
  let olu = [];                                   // her ogesi: olusturuldugu adim
  let ayrilan = CANLI, enYuksek = CANLI;
  for (let i = 1; i <= adim; i++) {
    for (let k = 0; k < GUNCELLEME; k++) olu.push(i);
    ayrilan = Math.max(ayrilan, CANLI + olu.length);
    enYuksek = Math.max(enYuksek, ayrilan);
    if (i % aralik === 0) {
      const sinir = (uzunIslem && i >= uzunIslem.bas && i <= uzunIslem.bit) ? uzunIslem.bas : i;
      olu = olu.filter((d) => d >= sinir);         // acik anlik goruntuden eskiler toplanir
    }
  }
  return { kalan: olu.length, sisme: enYuksek / CANLI };
}
console.log('— temizlik sıklığı —');
console.log('aralık (adım)   kalan ölü sürüm   şişme oranı');
for (const a of [1, 5, 25, 100, 600])
  { const s = calistir({ aralik: a });
    console.log(String(a).padStart(13), String(s.kalan).padStart(17), (s.sisme.toFixed(2) + '×').padStart(14)); }
console.log('— aynı temizlik (aralık 5), açık bir uzun işlemle —');
for (const uzunluk of [0, 100, 300, 600]) {
  const s = calistir({ aralik: 5, uzunIslem: uzunluk ? { bas: 1, bit: uzunluk } : null });
  console.log(`uzun işlem ${String(uzunluk).padStart(3)} adım →`, String(s.kalan).padStart(7),
              'ölü sürüm,', (s.sisme.toFixed(2) + '×').padStart(6), 'şişme');
}
JS
node temizlik.mjs
```

```
— temizlik sıklığı —
aralık (adım)   kalan ölü sürüm   şişme oranı
            1              2000          1.04×
            5              2000          1.12×
           25              2000          1.52×
          100              2000          3.02×
          600              2000         13.00×
— aynı temizlik (aralık 5), açık bir uzun işlemle —
uzun işlem   0 adım →    2000 ölü sürüm,  1.12× şişme
uzun işlem 100 adım →    2000 ölü sürüm,  3.10× şişme
uzun işlem 300 adım →    2000 ölü sürüm,  7.10× şişme
uzun işlem 600 adım → 1200000 ölü sürüm, 13.00× şişme
```

Birinci tablo beklendiği gibi: temizlik ne kadar seyrek çalışırsa ölü sürümler o kadar
birikir ve ayrılan en yüksek yer o kadar büyür. Her adımda çalışan bir temizlik şişmeyi
1,04'te tutuyor; altı yüz adımda bir çalışan ise 13,00'e çıkarıyor. Sona kalan ölü sürüm
sayısının hepsinde aynı olması yanıltmasın — ölçülen şey son durum değil, süreç boyunca
ulaşılan en yüksek yerdir; yer bir kez ayrıldıktan sonra geri verilmez.

İkinci tablo asıl uyarıyı veriyor. Temizlik sıklığı hiç değişmedi — her beş adımda bir
çalışıyor — ama üç yüz adım açık kalan tek bir işlem şişmeyi 1,12'den 7,10'a çıkardı.
Temizlik çalışıyor, ancak toplayacak bir şey bulamıyor: o işlemin anlık görüntüsünden sonra
üretilen bütün ölü sürümler hâlâ görünebilir durumda.

Sonuç iki cümlede toplanır. Şişmenin üst sınırını belirleyen şey, temizliğin çalışma sıklığı
ile **en eski açık işlemin yaşından** hangisi büyükse odur. Bu yüzden şişme sorunlarının
araştırılması, temizlik ayarlarından değil, uzun süredir açık kalan işlemlerin listesinden
başlar.

## Özet

- Ölü satır, artık hiçbir işlemin görmediği bir satır sürümüdür; hem silme hem güncelleme
  üretir. Şişme, kaplanan yerin gerekli yere oranıdır.
- Ölçümde satırların üçte biri silindiğinde sayfa sayısı ve dosya boyutu hiç değişmedi;
  yalnız sayfa içi doluluk %77,3'ten %54,2'ye düştü.
- Sürekli artan anahtarla eklenen yeni satırlar ortadaki boşlukları kullanamaz; 119.822
  satırlık boşluk varken 100.000 satır eklendiğinde dosya 2.723 sayfadan 3.403 sayfaya çıktı.
- Tam yeniden yazma doluluğu ilk günkü değere döndürdü (2.589 sayfa), ancak kilit, tablo
  boyutu kadar geçici alan ve tablo büyüklüğüyle orantılı süre gerektirir.
- Modelde şişme oranını belirleyen iki etken ayrıştı: temizliğin sıklığı ve en eski açık
  işlemin yaşı; üç yüz adım açık kalan tek bir işlem şişmeyi 1,12'den 7,10'a çıkardı.

## Sonraki Adım

Bu kursta şimdiye kadar sorulan her soru bir sayıyla yanıtlandı: sayfa sayısı, doluluk,
çerçeve sayısı, isabet oranı. Bu sayılar ortada durmuyor; motor kendi yapısı hakkındaki
bilgiyi sıradan tablolarda tutar ve sıradan sorgularla verir. Sonraki ders bu bilgiyi ele
alır: tabloların, sütunların, dizinlerin ve kısıtların tanımının nerede saklandığı, oradan
hangi yönetim sorularının yanıtlanabileceği ve şemayı sorgulayarak yazılan denetimlerin
elle tutulan listelerden neden üstün olduğu.
