---
title: 'Kilitleme Davranışı'
source: 'https://academia.sh/tr/kurslar/ileri-sql/kilitleme-davranisi'
course: 'İleri SQL'
language: tr
updated: '2026-08-17T18:08:55+00:00'
license: 'CC BY-SA 4.0'
---

# Kilitleme Davranışı

Paylaşımlı ve dışlayıcı kilitler, kilit ayrıntı düzeyi, iki bağlantıyla üretilen gerçek bir çakışma ve hata iletisi, yükseltme çakışması, kilitlenme döngüsü ve bekleme süresi sınırı.

Yalıtım düzeyleri neyin görüneceğini söyler, bedelini söylemez. Bedel, aynı veriye dokunan
işlemlerin birbirini beklemesidir. Beklemeyi yöneten mekanizma **kilittir (lock)**: bir
işlem bir kaynağa dokunmadan önce üzerinde hak alır, işi bitene kadar tutar, sonra bırakır.

Bu ders kilit türlerini tanımlar, iki bağlantıyla gerçek bir çakışma üretir ve motorun
verdiği hatayı okur.

## İki Kilit Türü

Temel model iki kilit türüne dayanır.

**Paylaşımlı kilit (shared lock)** okuma içindir. Aynı kaynak üzerinde birden çok
paylaşımlı kilit birlikte bulunabilir; okuyucular birbirini engellemez.

**Dışlayıcı kilit (exclusive lock)** yazma içindir. Bir kaynakta dışlayıcı kilit varsa
üzerine ne paylaşımlı ne başka bir dışlayıcı kilit konabilir.

Uyuşma kuralı tek cümleyle özetlenir: okuma okumayı engellemez, yazma her şeyi engeller.
Çok sürümlü eşzamanlılık denetimi kullanan motorlar bu kuralı okuma tarafında gevşetir —
okuyucular eski sürümü gördüğü için yazarı beklemez. Bu, İlişkisel Veritabanı Yönetimi
kursunda ele alınan bir gerçekleştirim seçimidir.

Kilidin **ne** üzerine konduğu ayrı bir eksendir: satır, sayfa, tablo ya da veritabanının
tamamı. İnce ayrıntı düzeyi eşzamanlılığı artırır ama kilit sayısını ve muhasebesini
büyütür. Aşağıdaki gözlemin yapıldığı motor, yazma için veritabanının tamamını kilitler;
satır kilidi kullanan bir motorda aynı senaryoda farklı satırlara yazan iki işlem birbirini
engellemez. Bu, motora göre değişen bir davranıştır ve taşınabilir kod yazarken bilinmesi
gerekir.

## Gerçek Bir Çakışma

Aşağıdaki betik aynı dosyaya iki bağlantı açar. Birinci bağlantı bir yazma işlemi açar ve
açık tutar; ikinci bağlantı yazmayı dener:

```bash
rm -f kilit.db kilit.db-wal kilit.db-shm
cat > kilit.mjs <<'JS'
import { DatabaseSync } from 'node:sqlite';

const a = new DatabaseSync('kilit.db');
const b = new DatabaseSync('kilit.db');
a.exec('PRAGMA journal_mode = WAL');
a.exec(`CREATE TABLE kitap(id INTEGER PRIMARY KEY, baslik TEXT, rafta INT);
        INSERT INTO kitap VALUES (1,'Kayip Zaman',1),(2,'Sessiz Ev',1);`);

a.exec('BEGIN IMMEDIATE');
a.exec('UPDATE kitap SET rafta = 0 WHERE id = 1');
console.log('A: yazma işlemi açık, 1 numaralı kitap güncellendi');

try {
  b.exec('BEGIN IMMEDIATE');
  b.exec('UPDATE kitap SET rafta = 0 WHERE id = 2');
  console.log('B: güncelledi');
} catch (hata) {
  console.log('B hata aldı:', hata.code, '-', hata.message);
}

console.log('B okuyabiliyor mu:',
            b.prepare('SELECT rafta FROM kitap WHERE id = 1').get().rafta);
a.exec('COMMIT');
console.log('A kesinleştirdi; B artık yazabilir');
b.exec('BEGIN IMMEDIATE');
b.exec('UPDATE kitap SET rafta = 0 WHERE id = 2');
b.exec('COMMIT');
console.log('son durum:', JSON.stringify(a.prepare(
  'SELECT id, rafta FROM kitap ORDER BY id').all()));
JS
node kilit.mjs
rm -f kilit.db kilit.db-wal kilit.db-shm
```

```text
A: yazma işlemi açık, 1 numaralı kitap güncellendi
B hata aldı: ERR_SQLITE_ERROR - database is locked
B okuyabiliyor mu: 1
A kesinleştirdi; B artık yazabilir
son durum: [{"id":1,"rafta":0},{"id":2,"rafta":0}]
```

Üç gözlem var.

Birincisi, B ikinci **kitaba** yazmak istiyordu, A ise birinciye yazmıştı. Yine de
engellendi: bu motorda yazma kilidi veritabanı düzeyindedir. Satır kilidi kullanan bir
motorda bu deneme başarılı olurdu.

İkincisi, hata iletisi bekleme değil **reddetme** bildiriyor. Bekleme süresi sınırı sıfır
olduğu için motor kuyruğa girmek yerine hemen döndü. İleti biçimi motora özgüdür; ortak
olan, çakışmanın uygulamaya bir hata olarak ulaşmasıdır.

Üçüncüsü, B yazamazken **okuyabildi** ve A'nın kesinleştirmediği değeri değil, eski değeri
gördü. Okuyucunun yazardan bağımsız ilerlemesi, önceki dersteki anlık görüntü davranışının
kilit tarafındaki karşılığıdır.

A kesinleştirdikten sonra B'nin aynı yazması sorunsuz geçti. Çakışma kalıcı bir engel
değil, zamanlamaya bağlı bir yarıştır.

## Yükseltme Çakışması

Daha ince bir durum, iki işlemin de önce okuyup sonra yazmasıdır. İkisi de okuma hakkıyla
başlar, sonra yazma hakkına **yükselmek** ister. Biri yükselirse diğerinin isteği
karşılanamaz:

```bash
rm -f kilit2.db kilit2.db-wal kilit2.db-shm
cat > kilitlenme.mjs <<'JS'
import { DatabaseSync } from 'node:sqlite';

const a = new DatabaseSync('kilit2.db');
const b = new DatabaseSync('kilit2.db');
a.exec('PRAGMA journal_mode = WAL');
a.exec(`CREATE TABLE kitap(id INTEGER PRIMARY KEY, baslik TEXT, rafta INT);
        INSERT INTO kitap VALUES (1,'Kayip Zaman',1),(2,'Sessiz Ev',1);`);

a.exec('BEGIN');
b.exec('BEGIN');
console.log('A okudu:', a.prepare('SELECT rafta FROM kitap WHERE id = 1').get().rafta);
console.log('B okudu:', b.prepare('SELECT rafta FROM kitap WHERE id = 1').get().rafta);

a.exec('UPDATE kitap SET rafta = 0 WHERE id = 1');
console.log('A yazdı');
try {
  b.exec('UPDATE kitap SET rafta = 0 WHERE id = 1');
  console.log('B yazdı');
} catch (hata) {
  console.log('B hata aldı:', hata.code, '-', hata.message);
}
b.exec('ROLLBACK');
a.exec('COMMIT');
console.log('son durum:', JSON.stringify(a.prepare(
  'SELECT id, rafta FROM kitap ORDER BY id').all()));
JS
node kilitlenme.mjs
rm -f kilit2.db kilit2.db-wal kilit2.db-shm
```

```text
A okudu: 1
B okudu: 1
A yazdı
B hata aldı: ERR_SQLITE_ERROR - database is locked
son durum: [{"id":1,"rafta":0},{"id":2,"rafta":1}]
```

İki işlem de kitabı rafta gördü; ikisi de ödünç vermek istedi. Yalıtım olmasaydı ikisi de
başarılı olur, kitap iki üyeye birden verilmiş görünürdü — önceki dersteki kayıp güncelleme.
Kilit bunu engelledi: biri kazandı, diğeri hata aldı.

Hata alan taraf için doğru davranış, işlemi geri alıp **yeniden denemektir**. Yeniden
denemede okuma güncel değeri getirir ve kitabın artık rafta olmadığı görülür. Çakışma
çözümü, uygulamanın bu yeniden deneme döngüsünü yazmasıyla tamamlanır; motorun tek başına
yaptığı, çakışmayı sessiz bırakmamaktır.

Bu deseni hatasız kurmanın standart yolu, okumayı baştan yazma niyetiyle yapmaktır.
Standart SQL bunun için okunan satırları kilitleyen bir yazım tanımlar:

```sql
SELECT rafta FROM kitap WHERE id = 1 FOR UPDATE;
```

Bu yazım, satırı okurken üzerine dışlayıcı hak alır; ikinci işlem okuma aşamasında beklemeye
girer ve yükseltme çakışması hiç oluşmaz. Yukarıdaki gözlemin yapıldığı motor bu yan
tümceyi tanımaz; onun karşılığı, işlemi `BEGIN IMMEDIATE` ile yazma niyetiyle açmaktır.
Deyimin kendisi burada çalıştırılmamıştır, biçimi gösterilmiştir.

## Kilitlenme

İki işlem birbirinin tuttuğu kaynağı beklerse hiçbiri ilerleyemez. Bu duruma **kilitlenme
(deadlock)** denir ve en yalın hâli iki kaynakla oluşur:

| Adım | İşlem A | İşlem B |
|---|---|---|
| 1 | `kitap` satırını kilitler | — |
| 2 | — | `uye` satırını kilitler |
| 3 | `uye` satırını ister, bekler | — |
| 4 | — | `kitap` satırını ister, bekler |

Dördüncü adımdan sonra bekleme çizgesinde bir çevrim vardır ve hiçbir bekleme kendiliğinden
sona ermez. Motorlar bunu iki yoldan çözer. Kilitlenme algılayan motorlar bekleme çizgesini
tarar, çevrim bulunca bir işlemi **kurban** seçip geri alır; kurbana bir kilitlenme hatası
döner. Algılama yapmayan motorlar bir bekleme süresi sınırı koyar; süre dolunca bekleyen
işlem hata alır. İkinci yolda gerçek bir kilitlenme ile yalnızca yavaş bir işlem birbirinden
ayırt edilemez.

Yukarıdaki gözlemde çevrim oluşmadı, çünkü o motor aynı anda tek yazara izin verir; ikinci
yazar beklemeye bile geçmeden reddedilir. Kilitlenme, ince ayrıntı düzeyinde kilit tutan
motorların sorunudur — daha çok eşzamanlılığın bedeli.

Önleme, çevrimin oluşmasını engellemekle olur:

- Kaynaklara **her zaman aynı sırayla** dokunmak. Her iki işlem de önce `kitap` sonra `uye`
  kilitlerse üçüncü ve dördüncü adım aynı yönde olur, çevrim kapanmaz.
- İşlemi kısa tutmak. Kilit tutma süresi kısaldıkça çakışma olasılığı düşer.
- Gerekiyorsa tüm kilitleri baştan almak; ilerledikçe hak yükseltmemek.

Bu üçü, çakışmayı yok etmez, olasılığını düşürür. Hata alma ihtimali kaldığı için,
çakışabilecek her işlemin yeniden denenebilir yazılması gerekir: aynı işlemi ikinci kez
çalıştırmanın ek bir yan etki üretmemesi, yani yinelemeye dayanıklı olması.

## Bekleme Süresi Sınırı

Reddetme ile bekleme arasındaki seçim bir ayardır. Bekleme süresi sınırı verilirse, çakışan
işlem hemen hata almak yerine belirtilen süre boyunca kilidin serbest kalmasını bekler:

```bash
rm -f kilit3.db kilit3.db-wal kilit3.db-shm
cat > bekleme.mjs <<'JS'
import { DatabaseSync } from 'node:sqlite';

const a = new DatabaseSync('kilit3.db');
const b = new DatabaseSync('kilit3.db', { timeout: 250 });
a.exec('PRAGMA journal_mode = WAL');
a.exec(`CREATE TABLE kitap(id INTEGER PRIMARY KEY, baslik TEXT, rafta INT);
        INSERT INTO kitap VALUES (1,'Kayip Zaman',1);`);

a.exec('BEGIN IMMEDIATE');
a.exec('UPDATE kitap SET rafta = 0 WHERE id = 1');

const basla = process.hrtime.bigint();
try {
  b.exec('BEGIN IMMEDIATE');
} catch (hata) {
  const gecen = Number(process.hrtime.bigint() - basla) / 1e6;
  console.log('B hata aldı:', hata.code);
  console.log('beklediği süre 250 ms sınırının üstünde mi:', gecen >= 250);
}
a.exec('COMMIT');
JS
node bekleme.mjs
rm -f kilit3.db kilit3.db-wal kilit3.db-shm
```

```text
B hata aldı: ERR_SQLITE_ERROR
beklediği süre 250 ms sınırının üstünde mi: true
```

Ölçülen sürenin kendisi makineye ve yüke bağlı olduğu için yazdırılmadı; yazdırılan, sınırın
aşılıp aşılmadığıdır. Sonuç, ilk gözlemdekiyle aynı hatadır — fark, hatanın hemen değil,
belirtilen süre boyunca beklendikten sonra gelmesidir.

Sınırın seçimi bir ödünleşimdir. Kısa sınır, çakışmayı hızlı bildirir ve uygulamanın
yeniden denemesine izin verir; uzun sınır, geçici çakışmaları uygulamaya hiç göstermez ama
gerçek bir kilitlenmede kaynak tutmayı uzatır. İkisi de işlemlerin kısa tutulması varsayımı
üzerine kuruludur.

## Özet

- Paylaşımlı kilit okuma, dışlayıcı kilit yazma içindir; okuma okumayı engellemez, yazma
  her şeyi engeller.
- Kilit ayrıntı düzeyi satırdan veritabanına kadar değişir; ince düzey eşzamanlılığı
  artırır, muhasebeyi büyütür.
- Gözlemde ikinci yazar `database is locked` hatası aldı ama okumayı sürdürebildi; çakışma
  kalıcı engel değil, zamanlamaya bağlı bir yarıştır.
- Okuyup sonra yazan iki işlem yükseltme çakışması üretir; standart çözüm okumayı yazma
  niyetiyle kilitlemek, uygulama çözümü işlemi geri alıp yeniden denemektir.
- Kilitlenme, bekleme çizgesinde çevrim oluşmasıdır; kaynaklara aynı sırayla dokunmak,
  işlemi kısa tutmak ve yeniden denenebilir yazmak önleyici alışkanlıklardır.

## Sonraki Adım

Bu derse kadar işlemin sınırını, hata durumunda ne yapılacağını ve yeniden denemeyi hep
uygulama kodu belirledi; veritabanı yalnız deyimleri aldı. Oysa bu mantığın bir kısmı
veritabanının içinde de tanımlanabilir: ödünç verme kuralı bir yordama konabilir, uygulama
o yordamı tek çağrıyla tetikleyebilir. Sonraki ders, sunucu tarafında çalışan yordam ve
işlevlerin standart biçimini, sağladıklarını ve getirdiği ödünleşimleri ele alacak.
