← Database Management System Ana Sayfa
Concurrency Control — Kapsamlı Türkçe Rehber
CENG208 — Veritabanı Yönetim Sistemleri

Concurrency
Control

Eş zamanlılık sorunları, kilit mekanizmaları, Two-Phase Locking, Deadlock yönetimi ve İzolasyon seviyeleri — eksiksiz Türkçe rehber.

3 Temel Sorun
Locking Protokolleri
Two-Phase Locking
Deadlock
İzolasyon Seviyeleri

Üç Eş Zamanlılık Sorunu

Çok işlemcili bir ortamda transaction'lar birbirleriyle girişim yapabilir. Her DBMS'nin önlemek zorunda olduğu üç temel eş zamanlılık sorunu vardır. Bu sorunların kökeni, transaction'ların aynı veri öğesine paralel erişimidir.

1

Problem 1

Lost Update

İkinci transaction, birinci transaction'ın değişikliğini yazmadan önce veriyi okur ve ardından kendi güncellemesiyle birincinin değişikliğinin üzerine yazar. Birincinin güncelleme kaybolur.

2

Problem 2

Uncommitted Dependency

İkinci transaction, henüz commit edilmemiş bir değişikliğe bağımlı hale gelir. Birinci transaction daha sonra rollback yaparsa, ikinci transaction geçersiz (kirli) bir veriyle çalışmış olur.

3

Problem 3

Inconsistent Analysis

Bir transaction veri okurken başka bir transaction aynı veriyi güncelleyip commit ederse, okuyan transaction tutarsız bir resim görür. Özellikle toplam/özet hesaplamalarında ciddi yanlış sonuçlara yol açar.

Problem 1: Lost Update (Kayıp Güncelleme)

Transaction A, t'yi okur (t1). Sonra Transaction B de t'yi okur (t2). A güncelleyip yazar (t3). Ardından B de güncelleyip yazar (t4). A'nın güncellemesi kaybolur!

Transaction A Zaman Transaction B
RETRIEVE tt1
t2RETRIEVE t
UPDATE tt3
t4UPDATE t ← A'nın güncellemesini ezer!

Fig. 16.1 — Transaction A, t4 anında güncellemesini kaybeder

⚠️

Neden olur? B, A'nın güncellemesini görmeden önce aynı verinin eski kopyasını okumuştur. B'nin yazması A'nın yazmasının üzerine gelir. Neden olduğu çakışma türü: WW (Write-Write)

Problem 2: Uncommitted Dependency (Taahhüt Edilmemiş Bağımlılık)

Transaction B, t'yi günceller (t1). Transaction A, henüz commit edilmemiş bu değeri okur (t2). Sonra B rollback yapar (t3). A artık var olmayan bir değere bağımlı kalmıştır.

Transaction A Zaman Transaction B
t1UPDATE t (commit edilmedi)
RETRIEVE t ← kirli veri!t2
t3ROLLBACK ← B geri alındı!

Fig. 16.2 — Transaction A, t2 anında commit edilmemiş değişikliğe bağımlı hale gelir

⚠️

Dirty Read (Kirli Okuma): A'nın t2'deki okuma işlemi bir "dirty read"dir. B rollback yapınca A'nın okuduğu değer sanki hiç var olmamış gibi olur. Neden olduğu çakışma türü: WR (Write-Read)

Problem 3: Inconsistent Analysis (Tutarsız Analiz)

ACC1=40, ACC2=50, ACC3=30 (Toplam=120). Transaction A toplamı hesaplarken Transaction B bazı hesapları güncelliyor. Sonuç: A'nın bulduğu toplam 110, gerçek ise 120!

Transaction A (Toplam hesaplıyor) Zaman Transaction B (Güncelleme yapıyor)
RETRIEVE ACC1 → sum=40t1
RETRIEVE ACC2 → sum=90t2
t3RETRIEVE ACC3
t4UPDATE ACC3: 30→20
t5RETRIEVE ACC1
t6UPDATE ACC1: 40→50
t7COMMIT
RETRIEVE ACC3 → sum=110 ❌ (110 değil 120 olmalı)t8

Fig. 16.4 — Transaction A tutarsız analiz yapar (110 bulur, 120 olmalı)

🔍

A, ACC1'i 40 olarak okumuştu. Oysa B, ACC1'i sonradan 50'ye çıkardı ve ACC3'ü 20'ye düşürdü. A bu değişikliği görmediğinden yanlış bir toplam hesapladı. Neden olduğu çakışma türü: RW (Read-Write)

Conflicts (Çakışmalar)

A ve B iki eş zamanlı transaction ise ve her ikisi de aynı veritabanı nesnesine (örneğin t tuple'ı) okuma veya yazma yapmak istiyorsa dört olasılık söz konusudur. Bu çakışmaların her biri farklı bir soruna yol açar.

RR Çakışması
✅ Sorun Yok

A okur, B de okur. Okuma işlemleri birbirine müdahale edemez. İki transaction da aynı anda okuyabilir, herhangi bir veri bozulması yaşanmaz.

RW Çakışması
⚠️ Inconsistent Analysis

A okur, B yazmak ister. B yazmasına izin verilirse Tutarsız Analiz problemi ortaya çıkabilir. A okuduğu değerin altında değişiklik yapılmış olur.

WR Çakışması
⚠️ Uncommitted Dep.

B yazar, A okumak ister. A okumasına izin verilirse Uncommitted Dependency problemi ortaya çıkabilir. A'nın bu okuma işlemi bir dirty read'dir.

WW Çakışması
❌ Lost Update

A yazar, B de yazmak ister. B yazmasına izin verilirse Lost Update problemi ortaya çıkabilir. A'nın güncellemesi B tarafından ezilir.

💡

Özet: RR çakışması zararsızdır. RW → Inconsistent Analysis, WR → Uncommitted Dependency (Dirty Read), WW → Lost Update sorununa yol açar. Bu üç sorunlu çakışmayı önlemek Concurrency Control'ün temel görevidir.

Locking (Kilitleme)

Bahsedilen tüm sorunlar, locking (kilitleme) adı verilen bir eş zamanlılık kontrol mekanizmasıyla çözülebilir. Bir transaction, eş zamanlılık sorunlarını önlemek için veritabanının belirli bir kısmını kilitler.

🔒 Exclusive Lock — X (Yazma Kilidi)

  • Yalnızca yazma işlemi için kullanılır
  • Başka hiçbir transaction aynı anda bu veriye erişemez
  • Ne S ne de X kilidi verilebilir
  • Tam izolasyon sağlar

📖 Shared Lock — S (Okuma Kilidi)

  • Okuma işlemi için kullanılır
  • Birden fazla transaction aynı anda S kilidi tutabilir
  • Yazma (X kilidi) engellenmiş olur
  • Eş zamanlı okumaya izin verir

Kilit Uyumluluk Matrisi (Lock Compatibility Matrix)

Bir transaction, talep ettiği kilit, başka transaction'ların o öğe üzerinde tuttuğu kilitlerle uyumluysa kilit verilebilir:

Mevcut \ Talep Edilen S (Shared) X (Exclusive)
S (Shared) ✅ true ❌ false
X (Exclusive) ❌ false ❌ false

Kural: Herhangi bir sayıda transaction aynı öğe üzerinde S kilidi tutabilir. Ancak herhangi bir transaction X kilidi tutuyorsa, başka hiçbir transaction (S veya X) kilit alamaz. X kilidi tam münhasır bir erişimdir.

Two-Phase Locking (2PL)

Bir kilitleme protokolü, bir transaction'ın veritabanındaki veri öğelerini ne zaman kilit alıp bırakabileceğini belirleyen kurallar bütünüdür. En yaygın ve güçlü protokol İki Aşamalı Kilitleme (2PL)'dir.

2PL, çakışma-serileştirilebilir (conflict-serializable) zamanlamaları garanti eder. Bu, eş zamanlı çalışan transaction'ların bir seri çalışmayla eşdeğer sonuç üretmesini sağlar.

Kilit Sayısı / Zaman

Kilit Sayısı ↑
Büyüme Fazı
Küçülme Fazı
→ Zaman
Başlangıç ↑ Lock Point (Maksimum) Commit/Rollback
Büyüme Fazı: Kilitler alınır, bırakılmaz
Küçülme Fazı: Kilitler bırakılır, alınmaz
Lock Point: Maksimum kilit noktası

📈 Faz 1: Growing (Büyüme Fazı)

  • Transaction yeni kilitler alabilir
  • Hiçbir kilit bırakılamaz
  • Kilit sayısı sadece artar
  • Lock point'e kadar sürer

📉 Faz 2: Shrinking (Küçülme Fazı)

  • Transaction kilitlerini bırakabilir
  • Yeni kilit alınamaz
  • Kilit sayısı sadece azalır
  • Commit/rollback'e kadar sürer

2PL Örneği: T₃₄ ve T₃₅

Önce kilidin eklenmediği, sonra eklendiği hali karşılaştıralım:

Kilit eklenmiş T₃₄
lock-S(A) ← S kilidi al read(A) lock-X(B) ← X kilidi al read(B) if A = 0 then B := B + 1 write(B) unlock(A) ← A kilidini bırak unlock(B) ← B kilidini bırak
Kilit eklenmiş T₃₅
lock-S(B) ← S kilidi al read(B) lock-X(A) ← X kilidi al read(A) if B = 0 then A := A + 1 write(A) unlock(B) ← B kilidini bırak unlock(A) ← A kilidini bırak

2PL Uzantıları

1
Basic Two-Phase Locking (Temel 2PL) Bir transaction, herhangi bir kilidi bırakmadan önce yeni kilit alamaz. Cascading rollback'lere karşı koruma sağlamaz.
2
Strict Two-Phase Locking (Katı 2PL) Transaction, commit veya abort olana kadar tüm exclusive (X) kilitlerini tutar. Cascading rollback'lerden kurtarılabilirliği garanti eder.
3
Rigorous Two-Phase Locking (Titiz 2PL) Transaction, commit veya abort olana kadar tüm kilitlerini (S ve X) tutar. Transaction'lar commit sırasına göre serileştirilebilir. Cascading rollback'ten tam koruma.

Deadlock (Kilitlenme)

Kilitleme protokollerinin çoğu deadlock'lara karşı koruma sağlamaz. Deadlock, her transaction'ın kümedeki başka bir transaction'ı beklediği döngüsel bir bekleme durumudur.

🔴

Klasik Deadlock Örneği (T₃ ve T₄): T₃, B üzerinde X kilidi tutuyor ve A'nın X kilidini bekliyor. T₄ ise A üzerinde S kilidi tutuyor ve B'nin S kilidini bekliyor. Her ikisi de ilerleyemez — sonsuz bekleme!

T₃
lock-X(B) ← B üzerinde X kilidi alındı read(B) B := B − 50 write(B) lock-X(A) ← A'yı bekliyor! 🔴
T₄
lock-S(A) ← A üzerinde S kilidi alındı read(A) lock-S(B) ← B'yi bekliyor! 🔴

Çözüm: T₃ veya T₄'ten biri rollback yapılmalı ve kilitleri serbest bırakılmalı. Hangisinin rollback yapılacağı victim selection (kurban seçimi) problemidir. Genellikle daha az iş yapmış (ya da daha ucuz rollback maliyetli) olan transaction seçilir.

Deadlock'ı Önleme Stratejileri

⏳ Wait-Die (Non-Preemptive)

  • T1 daha yaşlıysa bekleyebilir (wait)
  • T2 daha gençse rollback yapılır (die)
  • Yaşlı transaction genç olanı bekleyebilir
  • Genç transaction asla bekleyemez, ölür
  • Bir transaction lock almadan önce birkaç kez ölebilir

🗡️ Wound-Wait (Preemptive)

  • T1 daha yaşlıysa T2'yi rollback yapar (wound)
  • T2 daha yaşlıysa bekleyebilir (wait)
  • Yaşlı transaction, genç olanı zorla geri alır
  • Genç transaction yaşlı olanı bekleyebilir
  • Wait-Die'a göre daha az rollback yapar
💡

Her iki şemada da: Rollback yapılan bir transaction, orijinal timestamp'iyle yeniden başlatılır. Bu sayede yaşlı transaction'ların her zaman önceliği olur ve starvation önlenir. Net etki: en yaşlı transaction kazanır.

Örnek: Wait-Die Şeması

T1 (Daha yaşlı)OlayT2 (Daha genç)
lock-S(A) ✅
read(A)
lock-S(B) ✅
read(B)
lock-X(B) → T1 yaşlı, bekleyebilir ⏳
lock-X(A) → T2 genç, rollback! ❌
lock-X(B) ✅ (T2 rollback yaptı, T1 devam eder)

Wait-Die: T1 yaşlı olduğu için bekler, T2 genç olduğu için rollback yapılır

Timeout-Based (Zaman Aşımı Tabanlı) Strateji

⏱️ Timeout Tabanlı Deadlock Önleme

  • Bir transaction kilidi yalnızca belirli bir süre bekler
  • Süre dolunca transaction rollback yapılır
  • Deadlock olursa zaman aşımıyla çözülür
  • Uygulaması basittir
  • ⚠️ Deadlock olmasa bile gereksiz rollback yapabilir
  • ⚠️ İdeal timeout değerini belirlemek zordur
  • ⚠️ Starvation (açlık) hâlâ mümkündür

Deadlock Tespiti: Wait-for Graph

Deadlock'ları önlemek yerine tespit etmek de bir seçenektir. Wait-for Graf ile deadlock tespiti yapılabilir:

Wait-for Graf Kuralları
  • Her transaction grafta bir düğüm (vertex)'tür
  • Tᵢ → Tⱼ yönlü kenar: Tᵢ, Tⱼ'nin tuttuğu çakışan bir kilidi bekliyor demektir
  • Graf bir döngü içeriyorsa sistem deadlock durumundadır
  • Periyodik olarak döngü-tespit algoritması çalıştırılır
  • Döngü bulunursa kurbana rollback yapılır
✅ Döngü Yok — Deadlock Yok
T₁₇
T₁₈
T₂₀
T₁₇
T₁₉
T₁₈

Döngü yok → Normal çalışma, deadlock yok.

🔴 Döngü Var — Deadlock!
T₁₇
T₁₈
T₂₀
T₁₉
T₁₈
← Döngü!

T₁₈ ↔ T₁₉ arasında döngü → Deadlock! Bir kurban seçilmeli.

Starvation (Açlık / Aç Kalma)

Concurrency Control Manager kötü tasarlanmışsa, Starvation da mümkündür. Starvation, bir transaction'ın hiçbir zaman gerekli kilidi alamaması ve sürekli beklemesidir.

⚡ Starvation Senaryosu 1

  • T₁ bir öğe üzerinde X kilidi bekliyor
  • Art arda gelen transaction'lar aynı öğeye S kilidi istiyor
  • S kilitleri birbirleriyle uyumlu olduğundan hepsi onaylanıyor
  • T₁'in X kilidi için hiçbir zaman fırsat çıkmıyor
  • T₁ sonsuza kadar bekler!

🔄 Starvation Senaryosu 2

  • Bir transaction her seferinde deadlock kurbanı seçiliyor
  • Rollback → yeniden başlatma → tekrar deadlock kurbanı
  • Transaction hiçbir zaman tamamlanamıyor
  • Kötü victim selection algoritması neden olur
🛡️

Çözüm: Concurrency Control Manager, açlığı önleyecek şekilde tasarlanabilir. Örneğin FIFO (First-In First-Out) bekleme kuyruğu kullanılabilir: Bir transaction kilidi beklerken sıraya girer; sonraki S kilidi talepleri o transaction önce onaylanana kadar kuyrukta bekletilir.

Locking ile Sorunların Çözümü

Lost Update — Kilit ile Çözüm

Her iki transaction da güncelleme yapmadan önce X kilidi istediğinde, kilitleme mekanizması bir tarafı bekletir:

Transaction AZamanTransaction B
RETRIEVE t (S kilidi alır)t1
t2RETRIEVE t (S kilidi alır)
UPDATE t → X kilidi ister → bekler ⏳t3
t4UPDATE t → X kilidi ister → bekler ⏳
⚠️ Deadlock oluştu! (t4 anında) — Güncelleme kaybolmaz ama deadlock çözülmeli.

Fig. 16.6 — Kilit kullanıldığında güncelleme kaybolmaz ama deadlock oluşabilir

Uncommitted Dependency — Kilit ile Çözüm

Transaction AZamanTransaction B
t1UPDATE t (X kilidi alır)
RETRIEVE t → S kilidi ister → bekler ⏳t2
t3COMMIT / ROLLBACK (X kilidi bırakılır)
t4: RETRIEVE t → S kilidi alır ✅ (temiz veriyi okur)t4

Fig. 16.7 — Kilit sayesinde A, commit edilmemiş değişikliği göremez

Isolation Levels (İzolasyon Seviyeleri)

İzolasyon seviyesi, bir transaction çalışırken diğer transaction'lardan ne ölçüde etkileneceğini belirler. Yüksek izolasyon = daha az girişim = daha az eş zamanlılık (performans düşer). Düşük izolasyon = daha fazla girişim = daha yüksek performans ama riskler artar.

Read Anomalileri (Okuma Anormallikleri)

Anomali 1

Dirty Read

Bir transaction, başka bir transaction tarafından henüz commit edilmemiş değerleri okur. O transaction rollback yaparsa, okunan veri hiç var olmamış olur.

Anomali 2

Nonrepeatable Read

Bir transaction aynı nesneyi iki kez okur ve farklı değerler görür; oysa kendisi bu değeri değiştirmemiştir. Başka bir transaction araya girip güncelleme yapmıştır.

Anomali 3

Phantom Read

Bir transaction bir sorguyu yeniden çalıştırır ve koşulu karşılayan satır kümesinin değiştiğini görür. Başka bir transaction yeni satır eklemiş/silmiştir.

İzolasyon Seviyeleri Tablosu

İzolasyon Seviyesi Dirty Read Nonrepeatable Read Phantom Read Açıklama
READ UNCOMMITTED ✗ Evet ✗ Evet ✗ Evet Commit edilmemiş veriler bile okunabilir. En düşük izolasyon, en yüksek performans.
READ COMMITTED ✓ Hayır ✗ Evet ✗ Evet Yalnızca commit edilmiş veriler okunabilir. Çoğu veritabanının varsayılan seviyesi.
REPEATABLE READ ✓ Hayır ✓ Hayır ✗ Evet Okunan satırlar transaction boyunca değişmez. Phantom hâlâ mümkün.
SERIALIZABLE ✓ Hayır ✓ Hayır ✓ Hayır En yüksek izolasyon. Tam seri çalışmayla eşdeğer. En düşük performans.
⚖️

Trade-off: İzolasyon seviyesi ne kadar yükselirse, transaction'lar arasındaki girişim o kadar azalır ama eş zamanlılık (ve dolayısıyla verimlilik) de düşer. Uygulama gereksinimlerine göre doğru seviye seçilmelidir. Çoğu DBMS varsayılan olarak READ COMMITTED kullanır.

İzolasyon Seviyeleri Görsel Özeti

En Düşük ↓
READ UNCOMMITTED
Dirty ✗ Nonrep ✗ Phantom ✗
🚀 En yüksek performans
Düşük ↓
READ COMMITTED
Dirty ✓ Nonrep ✗ Phantom ✗
⚙️ Varsayılan seviye
Yüksek ↑
REPEATABLE READ
Dirty ✓ Nonrep ✓ Phantom ✗
🔒 MySQL varsayılanı
En Yüksek ↑
SERIALIZABLE
Dirty ✓ Nonrep ✓ Phantom ✓
🛡️ Tam güvenlik

Concurrency Control — Tam Özet

Veritabanında eş zamanlılık sorunları kilitleme protokolleri ile yönetilir. 2PL conflict-serializability garanti eder, deadlock özel stratejilerle ele alınır, izolasyon seviyeleri performans-güvenlik dengesini ayarlar.

Lost Update Uncommitted Dependency Inconsistent Analysis RR Conflict RW Conflict WR Conflict WW Conflict Shared Lock (S) Exclusive Lock (X) Lock Compatibility Two-Phase Locking Growing Phase Shrinking Phase Strict 2PL Rigorous 2PL Deadlock Wait-Die Wound-Wait Wait-for Graph Starvation Dirty Read Nonrepeatable Read Phantom Read Read Uncommitted Read Committed Repeatable Read Serializable