Bir siparişi kaydederken stoğu düşüyor, cari hesaba borç yazıyor ve fatura oluşturuyorsunuz. İkinci adımdan sonra elektrik kesilirse ne olur? Stok düşmüş ama sipariş yoktur. Transaction, bu tür yarım kalmış işlemleri imkânsız kılar: ya tüm adımlar gerçekleşir ya hiçbiri.
ACID
| Özellik | Anlamı | Örnek |
|---|---|---|
| Atomicity (Bütünlük) | İşlem bölünmez | Havalede para bir hesaptan çıkıp diğerine girmezse hiçbir şey olmaz |
| Consistency (Tutarlılık) | Kurallar her zaman korunur | Stok negatife düşmez, yabancı anahtar bozulmaz |
| Isolation (Yalıtım) | Eşzamanlı işlemler birbirini bozmaz | İki kasiyer aynı son ürünü satamaz |
| Durability (Kalıcılık) | Onaylanan işlem kaybolmaz | Sistem çökse bile kayıt yerindedir |
Kodda transaction
EF Core'da tek bir SaveChanges çağrısı zaten bir transaction içinde çalışır. Birden fazla kaydetme adımı varsa açıkça transaction başlatılır:
await using var tx = await db.Database.BeginTransactionAsync();
siparis.Onayla();
urun.StokDus(siparis.Adet);
await db.SaveChangesAsync();
await cariServis.BorcYazAsync(siparis); // aynı DbContext üzerinden
await db.SaveChangesAsync();
await tx.CommitAsync(); // hata olursa commit edilmez, tümü geri alınır
Eşzamanlılık sorunları
- Kirli okuma: Henüz onaylanmamış bir değişikliği başka işlemin görmesi.
- Tekrarlanamayan okuma: Aynı işlem içinde aynı satırı iki kez okuyunca farklı değer görmek.
- Kayıp güncelleme: İki kullanıcı aynı kaydı açar, ikisi de kaydeder; ilkinin değişikliği kaybolur.
İzolasyon seviyeleri
Veritabanları, yalıtım ile performans arasında farklı dengeler sunar: Read Uncommitted, Read Committed, Repeatable Read, Serializable ve satır sürümlemeye dayalı Snapshot. SQL Server ve PostgreSQL'in varsayılanı Read Committed'dır ve çoğu iş uygulaması için uygundur. Daha yüksek seviyeler daha güvenlidir ama daha çok kilit ve bekleme demektir.
Kayıp güncellemeyi önlemek: İyimser eşzamanlılık
Her satıra bir sürüm alanı (rowversion) eklenir. Kaydederken "bu satır ben okuduğumdan beri değişti mi?" kontrol edilir; değiştiyse kullanıcıya uyarı verilir. EF Core bunu yerleşik destekler ve stok, bakiye gibi kritik alanlarda çok değerlidir.
Deadlock
İki işlem birbirinin kilitlediği kaynağı beklediğinde ortaya çıkar; veritabanı birini kurban seçip iptal eder. Azaltmak için:
- Transaction'ları kısa tutun; içinde kullanıcı girişi, dosya işlemi veya dış API çağrısı beklemeyin.
- Tablolara her yerde aynı sırayla erişin.
- Doğru indekslerle kilitlenen satır sayısını azaltın.
- Kurban seçilen işlemi güvenle tekrar deneyin (idempotency).
Dağıtık sistemlerde
Transaction tek bir veritabanı içinde geçerlidir. Veritabanı kaydı ile e-posta gönderimi veya başka bir servis çağrısını aynı transaction'a sokamazsınız. Bunun için "outbox" deseni kullanılır: mesaj önce aynı transaction içinde bir tabloya yazılır, sonra arka planda gönderilir. Bkz. mesaj kuyruğu.
Sık sorulan sorular
NoSQL veritabanlarında transaction var mı?
Birçok modern NoSQL veritabanı belirli kapsamlarda transaction destekler; ancak ilişkisel veritabanları kadar geniş değildir. Bkz. SQL ve NoSQL.
Her sorguyu transaction içine almalı mıyım?
Hayır. Yalnızca birlikte başarılı olması gereken birden fazla değişiklik varsa açık transaction gerekir.
Sonuç
Transaction'lar, verinin yarım kalmasını ve eşzamanlı işlemlerin birbirini bozmasını önler. Kısa, odaklı transaction'lar ve iyimser eşzamanlılık kontrolü; güvenilir bir iş uygulamasının görünmez temelidir.