---
title: 'Yazma Öncesi Günlük'
source: 'https://academia.sh/tr/kurslar/veritabani-yonetimi/yazma-oncesi-gunluk'
course: 'İlişkisel Veritabanı Yönetimi'
language: tr
updated: '2026-08-17T18:08:56+00:00'
license: 'CC BY-SA 4.0'
---

# Yazma Öncesi Günlük

Kesinleştirmenin veri sayfalarını diske yazmaya bağlanmasının maliyeti, yazma öncesi kuralın tanımı, günlük dosyasının kesinleştirmede büyüyüp veri dosyasının değişmemesi, kesinleştirilmiş verinin yalnız günlükte bulunduğunun gösterilmesi, çökme sonrası kurtarma ve günlük kipleriyle eşitleme ayarının ölçülen etkisi.

Önceki ders verinin diskte sayfalar hâlinde durduğunu gösterdi. Bir satır güncellendiğinde
önce bellekteki sayfa değişir. Peki o sayfa diske yazılmadan makine kapanırsa? İşlemler
konusunda kesinleştirilen bir değişikliğin kalıcı olduğu söylenmişti; bu söz, sayfa henüz
diskte değilken nasıl tutulur?

Akla gelen ilk cevap — kesinleştirme anında değişmiş bütün sayfaları diske yazmak — hem
pahalıdır hem de yetmez. Bu ders neden yetmediğini ve gerçekte ne yapıldığını ele alır.
Ölçülecek şey nettir: kesinleştirilmiş bir veri, kesinleştirmenin hemen ardından hangi
dosyanın içindedir?

## Sayfaları Yazmak Neden Çözüm Değil

Kesinleştirmede değişmiş sayfaları diske yazmanın üç ayrı sorunu vardır.

**Yazmalar dağınıktır.** Tek bir işlem birbirinden uzak sayfalara dokunabilir: bir ödünç
satırı, üyenin bulunduğu sayfa, üç ayrı dizinin farklı yaprakları. Bunları yazmak dosyanın
dört bir yanına küçük yazmalar yapmak demektir. Sıralı bir yazmanın maliyeti ise erişim
başına değil, bayt başınadır.

**Yazma bölünebilir.** Dört kilobaytlık bir sayfanın diske yazılması, donanım düzeyinde tek
bir bölünmez işlem olmayabilir. Yazmanın ortasında elektrik kesilirse sayfanın bir kısmı yeni,
bir kısmı eski olur. Bu, kaybolmuş bir güncellemeden kötüdür: bozulmuş bir sayfadır.

**Aynı sayfa tekrar tekrar yazılır.** Sık güncellenen bir sayfa her kesinleştirmede yeniden
yazılır. Oysa o sayfanın son hâlinin bir kez yazılması yeterlidir.

## Yazma Öncesi Kural

Çözüm, kalıcılığı veri sayfalarından ayırmaktır. Motor, değişikliği önce **yazma öncesi
günlüğe (write-ahead log)** yazar: dosyanın sonuna eklenen, ne değiştiğini anlatan sıralı
kayıtlar. Kural tek cümledir:

> Bir veri sayfası diske yazılmadan önce, o sayfadaki değişikliği anlatan günlük kaydı
> diske yazılmış olmalıdır.

Kesinleştirme, günlük kayıtlarının kalıcı olmasıyla tamamlanır. Veri sayfaları bellekte
kalabilir; onları yazmak sonraya bırakılır.

Bu düzenlemenin üç sorunu da çözmesi doğrudandır. Yazma sıralıdır, çünkü günlük yalnız
sonuna eklenir. Bölünmüş yazma tehlikesi ortadan kalkar, çünkü eksik yazılmış bir günlük
kaydı kurtarma sırasında tanınıp yok sayılır — veri sayfası ise henüz hiç dokunulmamıştır.
Sık güncellenen sayfa da tek seferde yazılır, çünkü sayfanın diske inmesi kesinleştirmeye
bağlı değildir.

Kurtarma buradan çıkar. Çökmeden sonra açılışta motor günlüğü okur ve veri dosyasına
yansımamış kesinleştirilmiş değişiklikleri uygular. Kesinleştirilmemiş olanlar uygulanmaz.
Sonuç, çökme anındaki kesinleştirilmiş duruma tam olarak dönmektir.

İlişkisel Kuram kursunda ACID'in kalıcılık maddesi anlatılırken bu yapıdan **önden yazma
kütüğü** adıyla söz edilmişti; aynı yapıdır, bu kurs boyunca katalogdaki adlandırma
kullanılacaktır.

## Günlük Dosyasının Ortaya Çıkışı

İddia ölçülebilir. Aşağıdaki koşum bir veritabanı kurar, günlük kipini açar, sonra beş bin
satır ekleyip iki dosyanın boyutuna bakar. Denetim noktası devre dışı bırakılmıştır; nedeni
sonraki dersin konusudur.

```sh
rm -f g.db g.db-wal g.db-shm
sqlite3 g.db <<'SQL'
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, '2024-01-01', NULL
  FROM (WITH RECURSIVE s(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM s WHERE n<50000) SELECT n FROM s);
PRAGMA journal_mode = WAL;
PRAGMA wal_autocheckpoint = 0;
UPDATE odunc SET iade_tarihi = '2024-02-01' WHERE odunc_id = 1;
.shell echo "--- baslangic ---"
.shell echo "veri dosyasi: $(wc -c < g.db)   gunluk: $(wc -c < g.db-wal)"
INSERT INTO odunc SELECT 100000+n, 1+(n*7)%200000, 1+(n*13)%120000, '2024-03-01', NULL
  FROM (WITH RECURSIVE s(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM s WHERE n<5000) SELECT n FROM s);
.shell echo "--- 5000 satirlik islem kesinlestikten sonra ---"
.shell echo "veri dosyasi: $(wc -c < g.db)   gunluk: $(wc -c < g.db-wal)"
SELECT count(*) AS toplam_satir FROM odunc;
SQL
```

```
wal
0
--- baslangic ---
veri dosyasi:  1380352   gunluk:    24752
--- 5000 satirlik islem kesinlestikten sonra ---
veri dosyasi:  1380352   gunluk:   173072
55000
```

İlk iki satır iki ayarın döndürdüğü değerlerdir: günlük kipi `wal` oldu, kendiliğinden
denetim noktası sıfırlandı. Asıl sonuç alttadır. Beş bin satırlık ekleme kesinleştikten
sonra veri dosyasının boyutu bir bayt bile değişmedi — 1.380.352 idi, 1.380.352 kaldı.
Günlük dosyası ise 24.752 bayttan 173.072 bayta çıktı. Sorgu ise yeni satırları görüyor:
55.000.

Dosya boyutları ortama ve sayfa boyutuna bağlıdır; değişmeyecek olan, hangi dosyanın
büyüdüğüdür.

## Kesinleştirilmiş Veri Nerede

Veri dosyasının değişmemesi, kesinleştirilmiş satırların orada olmadığı anlamına gelir.
Bunu doğrudan sınamanın yolu, dosyaları ayırıp ayrı ayrı açmaktır.

```sh
rm -f g.db g.db-wal g.db-shm yalniz_veri.db veri_ve_gunluk.db veri_ve_gunluk.db-wal
sqlite3 g.db <<'SQL'
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, '2024-01-01', NULL
  FROM (WITH RECURSIVE s(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM s WHERE n<50000) SELECT n FROM s);
PRAGMA journal_mode = WAL;
PRAGMA wal_autocheckpoint = 0;
INSERT INTO odunc SELECT 100000+n, 1+(n*7)%200000, 1+(n*13)%120000, '2024-03-01', NULL
  FROM (WITH RECURSIVE s(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM s WHERE n<5000) SELECT n FROM s);
.shell cp g.db yalniz_veri.db
.shell cp g.db veri_ve_gunluk.db
.shell cp g.db-wal veri_ve_gunluk.db-wal
SQL
echo "--- yalniz veri dosyasi kopyalandi ---"
sqlite3 yalniz_veri.db "SELECT count(*) FROM odunc;"
echo "--- veri dosyasi + gunluk kopyalandi ---"
sqlite3 veri_ve_gunluk.db "SELECT count(*) FROM odunc;"
```

```
wal
0
--- yalniz veri dosyasi kopyalandi ---
50000
--- veri dosyasi + gunluk kopyalandi ---
55000
```

Sonuç tartışmasızdır. Yalnız veri dosyası kopyalandığında elli bin satır görünüyor:
kesinleştirilmiş beş bin satır orada yok. Günlük de kopyalandığında elli beş bin satır
görünüyor. Kesinleştirilmiş veri, kesinleştirmenin hemen ardından günlüğün içindedir.

Bu gözlemin yedekleme tarafında doğrudan bir sonucu vardır ve bu kursun İşletim konusunda
yeniden karşılaşılacaktır: çalışan bir veritabanının dosya düzeyinde yedeği, günlüğü de
kapsamak zorundadır. Yalnız veri dosyasını kopyalayan bir yedek, en son kesinleştirilmiş
işlemleri içermez.

## Çökmeden Sonra Kurtarma

Kurtarmanın gerçekten çalıştığını görmek için gerçek bir çökme gerekir. Aşağıdaki betik iki
bin satırı tek bir işlemde kesinleştirir, sonra süreci hiçbir kapanış işlemi yapmadan
öldürür: dosyalar kapatılmaz, denetim noktası alınmaz, tampon boşaltılmaz.

```sh
rm -f c.db c.db-wal c.db-shm
cat > cokme.mjs <<'JS'
import { DatabaseSync } from 'node:sqlite';
const db = new DatabaseSync('c.db');
db.exec('PRAGMA journal_mode = WAL');
db.exec('PRAGMA wal_autocheckpoint = 0');
db.exec(`CREATE TABLE odunc(odunc_id INTEGER PRIMARY KEY, uye_id INT, alis_tarihi TEXT)`);
db.exec('BEGIN');
for (let i = 1; i <= 2000; i++) db.exec(`INSERT INTO odunc VALUES (${i}, ${1 + (i * 13) % 500}, '2024-03-01')`);
db.exec('COMMIT');
console.log('kesinleştirildi; işlem içindeki satır sayısı:',
            db.prepare('SELECT count(*) AS c FROM odunc').get().c);
process.kill(process.pid, 'SIGKILL');   // temizlik yapmadan, kapanış olmadan sonlan
JS
node cokme.mjs
echo "süreç çıkış kodu: $?"
echo "--- çökme anındaki dosyalar ---"
echo "veri dosyası: $(wc -c < c.db)   günlük: $(wc -c < c.db-wal)"
echo "--- veritabanı yeniden açılıyor ---"
sqlite3 c.db "SELECT count(*) FROM odunc;"
```

```
kesinleştirildi; işlem içindeki satır sayısı: 2000
süreç çıkış kodu: 137
--- çökme anındaki dosyalar ---
veri dosyası:     4096   günlük:    61832
--- veritabanı yeniden açılıyor ---
2000
```

Çıkış kodu 137, sürecin öldürülerek sonlandığını gösterir. Çökme anında veri dosyası tek
bir sayfa büyüklüğünde: ne tablo tanımı ne de satırlar oraya yazılmış. Her şey 61.832
baytlık günlüktedir. Veritabanı yeniden açıldığında iki bin satırın tamamı yerindedir.

Kalıcılığın nasıl sağlandığı budur. Kesinleştirme, veriyi son duracağı yere yazmak değil,
neyin değiştiğini geri döndürülemez biçimde kaydetmektir. Verinin yerine yerleşmesi
sonraki dersin konusudur.

## Günlük Kipi ve Eşitleme Ayarı

Günlük tutmanın tek bir biçimi yoktur ve seçim ölçülebilir bir fark yaratır. İki eksen
vardır.

Birinci eksen **günlük kipidir**. Yazma öncesi günlükte değişiklikler ayrı bir dosyaya
eklenir, veri dosyası olduğu gibi kalır. Bunun alternatifi **geri alma günlüğüdür (rollback
journal)**: değişiklikten önceki sayfanın bir kopyası ayrı dosyaya yazılır, sayfanın yeni
hâli doğrudan veri dosyasına işlenir; işlem geri alınırsa eski sayfalar geri yazılır.
Kipin adı ve seçenekleri motora özgüdür.

İkinci eksen **eşitleme düzeyidir**: kesinleştirmede motorun işletim sisteminden yazmanın
gerçekten diske indiğini beklemesi mi, yoksa yazmayı teslim edip devam etmesi mi.

```sh
cat > kip.mjs <<'JS'
import { DatabaseSync } from 'node:sqlite';
import { rmSync } from 'node:fs';

function olc(kip, esitleme) {
  for (const ek of ['', '-wal', '-shm', '-journal']) rmSync('k.db' + ek, { force: true });
  const db = new DatabaseSync('k.db');
  db.exec(`PRAGMA journal_mode = ${kip}`);
  db.exec(`PRAGMA synchronous = ${esitleme}`);
  db.exec('CREATE TABLE odunc(odunc_id INTEGER PRIMARY KEY, uye_id INT, alis_tarihi TEXT)');
  const ekle = db.prepare('INSERT INTO odunc VALUES (?, ?, ?)');
  const bas = process.hrtime.bigint();
  for (let i = 1; i <= 2000; i++) {              // her satır ayrı bir işlem
    db.exec('BEGIN');
    ekle.run(i, 1 + (i * 13) % 500, '2024-03-01');
    db.exec('COMMIT');
  }
  const sure = Number(process.hrtime.bigint() - bas) / 1e6;
  db.close();
  return sure;
}

const olculer = [['delete', 'FULL'], ['wal', 'FULL'], ['wal', 'NORMAL']];
const sonuc = olculer.map(([k, e]) => [k, e, olc(k, e)]);
const taban = sonuc[0][2];
console.log('günlük kipi   eşitleme   2000 işlem (ms)   orana göre');
for (const [k, e, s] of sonuc)
  console.log(k.padEnd(13), e.padEnd(10), s.toFixed(0).padStart(14), (taban / s).toFixed(1).padStart(12) + '×');
JS
node kip.mjs
```

```
günlük kipi   eşitleme   2000 işlem (ms)   orana göre
delete        FULL                  335          1.0×
wal           FULL                   60          5.6×
wal           NORMAL                 12          26.8×
```

Süreler ortama bağlıdır; okunacak olan oranlardır. Yazma öncesi günlük, geri alma günlüğüne
göre bu ortamda beş buçuk kat hızlı kesinleştirdi. Nedeni yazma biçimidir: geri alma
günlüğünde her işlem hem günlük dosyasını hem veri dosyasını değiştirir ve sonunda günlük
dosyasını siler; yazma öncesi günlükte tek bir dosyanın sonuna eklenir.

Üçüncü satır ise ödünleşimin kendisidir. Eşitleme gevşetildiğinde ölçüm yirmi yedi kat
hızlanır, çünkü kesinleştirme artık yazmanın diske indiğini beklemez. Bunun bedeli
kalıcılıktır: ani bir güç kesintisinde en son kesinleştirilmiş birkaç işlem kaybolabilir.
Veritabanı bozulmaz — günlük yapısı bunu engeller — ama "kesinleştirildi" denen bir işlem
geri gelmeyebilir. Bu ayar, ACID'in kalıcılık maddesinden bilerek vazgeçmek anlamına gelir
ve yalnız kaybı göze alınabilen veriler için savunulabilir.

## Özet

- Kesinleştirmede veri sayfalarını diske yazmak dağınık yazma, bölünmüş yazma ve aynı
  sayfanın tekrar tekrar yazılması sorunlarını doğurur.
- Yazma öncesi kural, bir veri sayfası diske inmeden önce o değişikliği anlatan günlük
  kaydının kalıcı olmasını ister; kesinleştirme günlüğün kalıcı olmasıyla tamamlanır.
- Ölçümde beş bin satırlık bir ekleme kesinleştiğinde veri dosyası hiç değişmedi, günlük
  dosyası 24.752 bayttan 173.072 bayta çıktı.
- Yalnız veri dosyası kopyalandığında 50.000, günlükle birlikte kopyalandığında 55.000
  satır görüldü: kesinleştirilmiş veri günlüğün içindedir.
- Öldürülen bir süreçten sonra veri dosyası tek sayfalık kalmasına rağmen iki bin satırın
  tamamı günlükten kurtarıldı.
- Günlük kipi ve eşitleme düzeyi ölçülebilir fark yaratır; eşitlemeyi gevşetmek bu ortamda
  yirmi yedi kat hızlandırdı ve karşılığında kalıcılıktan vazgeçti.

## Sonraki Adım

Bu dersteki koşumlarda kendiliğinden denetim noktası kapatılmıştı ve günlük dosyası hiç
küçülmeden büyüdü. Bu sürdürülebilir değildir: sınırsız büyüyen bir günlük diski doldurur
ve kurtarma süresini uzatır, çünkü açılışta okunacak kayıt sayısı artar. Günlükteki
değişikliklerin veri dosyasına işlenip günlüğün kısalması gerekir. Sonraki ders bu işlemi
ele alır: denetim noktası ne yapar, ne sıklıkta yapılmalıdır ve sıklık ile kurtarma süresi
arasındaki ödünleşim nasıl kurulur.
