TRANSACTİON ISOLATİON LEVELS NEDİR? DİRTY READ’DEN SERİALİZABLE’A PRATİK ETKİLER
Veritabanı tarafında “aynı anda birden fazla iş” yapmak, uygulamanın hızını artırır; fakat doğru kurgulanmazsa aynı hız, sessiz ve sinsi hatalara da kapı açar. Bir kullanıcının baktığı stok miktarı, başka bir işlem tarafından güncellenirken; rapor ekranındaki toplamlar, ödeme adımları, rezervasyon sayıları ve kampanya limitleri beklenmedik şekilde dalgalanabilir. İşte bu noktada transaction isolation levels, eşzamanlılık (concurrency) ile doğruluk (consistency) arasındaki dengeyi kurmanın ana aracıdır.
Isolation level seçimi, “veri her zaman en doğru haliyle mi okunmalı, yoksa daha yüksek throughput için belirli anormallikleri tolere edebilir miyiz?” sorusuna verilen pratik bir cevaptır. Bazı sistemlerde yanlış bir seviye seçimi, nadiren görülen ama en pahalıya patlayan “aralıklı hata” türlerini doğurur: arada bir tutmayan bakiyeler, beklenmedik limit aşımı, “az önce gördüğüm değer şimdi neden değişti?” şikâyeti gibi.
Bu yazıda Dirty Read’den başlayıp Serializable’a uzanan isolation seviyelerini, hangi anomalileri engellediklerini ve gerçek dünyada performans ile kilitlenme davranışına etkilerini ele alacağız. Ayrıca test edilebilir, somut SQL örnekleriyle, her seviyenin neyi garanti edip neyi etmediğini netleştireceğiz.

Transaction Isolation Levels: Temel fikir ve neden gerekli?
Bir transaction, bir iş birimini atomik bir bütün olarak ele alır. Ancak aynı tabloda aynı anda birden fazla transaction çalıştığında, “ben okurken başkası yazarsa ne olur?” sorusu doğar. Isolation seviyeleri, transaction’ların birbirinin ara durumlarını ne kadar görebileceğini belirler. Bu, sadece “görüntü” meselesi değildir; aynı zamanda kilitlerin ne kadar tutulacağı, hangi satırların bloke edileceği ve sistemin ne kadar paralelleşebileceği anlamına gelir.
İzolasyonun yükselmesi genellikle daha güçlü doğruluk garantileri verir; fakat bedeli çoğu zaman daha fazla blokaj, daha uzun bekleme, daha az throughput ve bazı durumlarda daha yüksek deadlock olasılığıdır. Öte yandan izolasyonun düşmesi, daha iyi performans verebilir; ancak uygulama mantığınız “okuduğum değer, işlem bitene kadar değişmez” varsayımına dayanıyorsa ciddi problemler çıkarabilir.
ACID içinde Isolation neyi garanti eder?
ACID’in “I” harfi olan Isolation, transaction’ların birbirinden soyutlanmasını ifade eder. Bu soyutlama, her veritabanında aynı teknikle yapılmaz: kimi sistemlerde ağırlıklı olarak kilitleme (locking), kimilerinde MVCC (Multi-Version Concurrency Control) kullanılır. Sonuçta amaç, belirli okuma anomalilerini engellemek ve uygulamanın beklentileriyle uyumlu bir çalışma modeli sağlamaktır.
Anomali dili: Sorunu doğru isimlendirmek
Isolation seviyelerini anlamanın en hızlı yolu, engelledikleri anomalileri net tanımlamaktır: Dirty Read, Non-Repeatable Read ve Phantom Read. Bu kavramlar, “okuma sırasında ne kadar sürprizle karşılaşırım?” sorusuna yanıt verir. Bir seviyeyi seçerken, önce hangi anomalilerin iş mantığınızda kabul edilemez olduğunu belirlemek gerekir.

Dirty Read nedir ve neden tehlikelidir?
Dirty read, bir transaction’ın diğer bir transaction’ın henüz commit etmediği (hatta ileride rollback edebileceği) değişiklikleri okumasıdır. Bu durum, “sonuçtan emin olmadan sonucu görmek” gibidir. Özellikle finansal işlemler, stok yönetimi ve limit kontrollerinde dirty read çoğu zaman kabul edilemez; çünkü rollback olduğunda sistemin başka yerleri “olmamış bir güncellemeye” göre karar vermiş olur.
Dirty Read senaryosu: Commit edilmemiş veriyi okumak
Aşağıdaki örnekte, T1 bir fiyatı güncelliyor ama henüz commit etmiyor. T2 ise aynı kaydı okuyor. Eğer isolation seviyesi buna izin verirse, T2 gerçekte hiç gerçekleşmeyecek bir değere göre işlem yapabilir.
-- Transaction T1
BEGIN;
UPDATE products SET price = 999 WHERE id = 42;
-- Henüz COMMIT yok, hatta rollback olabilir
-- Transaction T2 (aynı anda)
BEGIN;
SELECT price FROM products WHERE id = 42; -- 999 görülebilir (dirty read)
COMMIT;
-- T1 karar değiştirirse
ROLLBACK;Bu örnekte T2’nin gördüğü 999, sistemin kalıcı gerçeği olmayabilir. T2 bu değere göre kampanya hesapladıysa veya “fiyat uygun” diyerek başka bir tabloya kayıt attıysa, uygulama iç tutarlılığını kaybeder.
Read Uncommitted: Hızlı ama riskli bir yaklaşım
Read Uncommitted, adından da anlaşılacağı gibi commit edilmemiş değişiklikleri okumaya izin veren en düşük izolasyon seviyesidir. Bu seviye, bazı raporlama veya “yaklaşık değer yeterli” senaryolarında tercih edilebilir; ancak iş kritik verilerde tehlikelidir. Gerçek hayatta pek çok ekip, Read Uncommitted’ı ya hiç kullanmaz ya da çok sınırlı alanlarda kontrollü şekilde uygular.
Ne zaman Read Uncommitted düşünülebilir?
- Gerçek zamanlı dashboard’larda, sayılar birkaç saniye “oynayabilir” ise
- Geçici veri akışlarında, doğrulama başka katmanda yapılıyorsa
- Yük altında “en hızlı okuma” istenip, sonuçlar karar mekanizmasına doğrudan bağlanmıyorsa
Yine de bu seçimde, uygulama tarafında “gördüğüm değer kesin değil” düşüncesiyle hareket etmek gerekir. Aksi halde sistem, hatayı nadiren üretir ama ürettiğinde teşhisi zor olur.
Read Committed: Birçok sistemin varsayılan dengesi
Read Committed, commit edilmemiş veriyi okumayı engeller; yani dirty read riskini ortadan kaldırır. Bu nedenle pek çok veritabanında yaygın varsayılan seviye olarak görülür. Ancak Read Committed, aynı transaction içinde aynı satırı iki kez okuduğunuzda farklı sonuç görmenizi engellemez; bu da non-repeatable read anomalisine yol açabilir.
Non-Repeatable Read: Aynı satır, farklı an
Bir transaction içinde aynı satırı iki defa okuduğunuzda, arada başka bir transaction o satırı güncelleyip commit ederse ikinci okuma farklı değer dönebilir. Uygulama, “ben bu kullanıcıyı zaten doğrulamıştım” ya da “ben bu bakiyeyi biraz önce gördüm” varsayımıyla ilerliyorsa sorun çıkar.
-- Transaction T1
BEGIN;
SELECT balance FROM accounts WHERE id = 7; -- örn: 1000
-- Transaction T2 (araya girer)
BEGIN;
UPDATE accounts SET balance = balance - 200 WHERE id = 7;
COMMIT;
-- Transaction T1 devam eder
SELECT balance FROM accounts WHERE id = 7; -- artık 800 (non-repeatable read)
COMMIT;Bu davranış bazen kabul edilebilir, bazen değildir. Örneğin bir ekran yenilemesi için sorun olmayabilir; fakat “ilk okuduğum değere göre limit kontrolü” yaptıysanız ikinci okuma farklı çıktığında akış bozulabilir.
Repeatable Read: Aynı satırın tekrar okunabilirliği
Repeatable Read, aynı transaction içinde aynı satırı tekrar okuduğunuzda aynı sonucu almayı hedefler; yani non-repeatable read’i engeller. Uygulama açısından bu, “okuduğum satır işlem boyunca sabit” algısını güçlendirir. Ancak Repeatable Read’in her veritabanında aynı davranışı göstermediğini bilmek önemli: MVCC kullanan sistemlerde bu garanti farklı tekniklerle sağlanırken, kilit tabanlı sistemlerde daha agresif kilitleme görülebilir.
Phantom Read: Satırlar değişmiyor ama sonuç kümesi değişiyor
Repeatable Read, belirli satırların tekrar okunabilirliğini garanti ederken, “bir koşula uyan satırların bütünü” için tam garanti vermeyebilir. Örneğin “bu tarih aralığında kaç sipariş var?” sorgusunu aynı transaction içinde iki kez çalıştırdığınızda, araya giren başka bir transaction yeni bir satır ekleyip commit ederse sonuç kümesi büyüyebilir. Bu yeni satıra “phantom” denir.
Phantom read, özellikle sıra numarası üretimi, kapasite kontrolü ve “limitli kayıt” senaryolarında kritiktir. “Şu koşula uyan kayıt sayısı 10’dan küçükse ekle” gibi akışlarda, phantom read kontrol edilmezse yarış koşulları (race condition) oluşabilir.

Serializable: En güçlü izolasyon, en yüksek maliyet
Serializable, teorik olarak transaction’ların sanki tek tek, sırayla çalışıyormuş gibi sonuç vermesini hedefler. Bu seviye, dirty read ve non-repeatable read’i engellediği gibi, phantom read’i de kontrol altına alır. Böylece “koşula uyan kayıt kümesi” işlem boyunca tutarlı kalır. En basit anlatımla serializable, uygulamaya “beklenmedik sürpriz yok” hissini verir.
Serializable ile güvenlik: İş kuralları daha basit hale gelir
Serializable seviyesinde, birçok karmaşık yarış koşulu uygulama katmanında ekstra kilit yazmadan engellenebilir. Ancak bu rahatlığın bedeli vardır: Daha fazla blokaj, daha uzun beklemeler, daha sık yeniden deneme (retry) ihtiyacı ve bazı sistemlerde daha yüksek abort oranı. Bu yüzden serializable, her sorgu için “default” seçmekten ziyade, gerçekten ihtiyaç duyulan kritik bölgelere uygulanır.
Snapshot Isolation ve MVCC: Okumayı kilitlemeden tutarlılık
Güncel veritabanlarının önemli bir kısmı, okuma işlemlerini yazma işlemleriyle minimum seviyede çakıştırmak için MVCC yaklaşımını kullanır. MVCC’de okuma, çoğu zaman verinin “anlık görüntüsünü” (snapshot) görür; başka transaction’ların commit ettiği yeni versiyonlar, sizin transaction’ınızı etkilemez. Bu, özellikle yoğun okuma yüklerinde yüksek performans sağlar.
Snapshot Isolation hangi anomalileri engeller, hangilerini bırakır?
Snapshot isolation, dirty read ve çoğu non-repeatable read benzeri sürprizi azaltır; çünkü aynı snapshot üzerinden okursunuz. Ancak bazı yazma-yazma çatışmaları veya iş kuralına bağlı yarış koşulları hâlâ görülebilir. Örneğin “write skew” denilen senaryolar, snapshot isolation altında mümkün olabilir; bu nedenle kritik kısımlarda serializable tercih edilebilir veya uygulama mantığı ek kontrollerle güçlendirilir.
Pratik seçim rehberi: Hangi durumda hangi seviye?
Isolation level seçimi, “doğruluk” ile “performans” arasındaki pazarlığın adıdır. Tek bir doğru yoktur; işin doğası, veri hacmi, sorgu profili ve hata toleransı belirleyicidir. Burada amaç, en pahalı seviyeyi seçmek değil; iş kuralına uygun minimum garantiyi sağlamaktır.
Önerilen yaklaşım: Kritik akışlara odaklanın
- Önce iş kurallarını çıkarın: “Bu değer işlem boyunca değişmemeli” mi?
- Anomalileri eşleyin: Dirty read mi, phantom read mi daha tehlikeli?
- En düşük güvenli seviyeyi seçin: Gereksiz yere blokaj üretmeyin.
- Yük testinde izleyin: Bekleme, timeout ve deadlock metriklerini inceleyin.
- Gerekirse retry stratejisi ekleyin: Özellikle yüksek izolasyonlarda.
Performans, kilitler ve deadlock: Görünmeyen maliyetler
İzolasyon seviyesi yükseldikçe, veritabanı daha fazla “koruma” uygular. Kilit tabanlı sistemlerde bu, daha uzun süre kilit tutmak anlamına gelir. MVCC tabanlı sistemlerde okuma çoğu zaman bloklanmasa da, yazma çatışmaları ve versiyon yönetimi maliyetleri artabilir. Ayrıca uzun süren transaction’lar, hem kilitleri hem de versiyon temizleme süreçlerini olumsuz etkiler.
Deadlock riskini azaltmak için pratik ipuçları
- Transaction sürelerini kısaltın: Gereksiz beklemeleri dışarı alın.
- Kaynakları tutarlı sırayla kilitleyin: Aynı tablolar aynı sırada erişilsin.
- Gereksiz geniş sorgulardan kaçının: Daraltılmış WHERE koşulları tercih edin.
- İndeksleri gözden geçirin: Kilit kapsamını küçültmek çoğu zaman mümkündür.
Uygulama seviyesinde doğru kullanım: “Seviyeyi ayarla ve unut” değil
Isolation level, çoğu zaman oturum (session) veya transaction bazında ayarlanabilir. Bu esneklik, aynı uygulama içinde farklı akışların farklı ihtiyaçlarına cevap verir. Örneğin raporlama ekranları Read Committed ile çalışırken, limit ve rezervasyon gibi kritik akışlar daha yüksek seviyeye alınabilir. Buradaki kritik nokta, seçimin dokümante edilmesi ve “neden”inin net olmasıdır.
Öğrenmeyi hızlandırmak için kaynak: pratik SQL odaklı çalışma
Isolation seviyelerini gerçekten kavramanın en iyi yolu, iki oturum açıp aynı tabloda farklı işlemleri denemektir. Eğer konuyu SQL tarafında daha sistematik öğrenmek ve transaction yönetimini gerçek senaryolarla pekiştirmek isterseniz SQL Programlama Eğitimi içeriği, transaction mantığı ve eşzamanlılık pratiklerini daha derinlemesine ele alır.
Özet: Doğruluk hedefiyle uyumlu izolasyon seçimi
Transaction isolation levels, eşzamanlı işlemlerde hangi sürprizlere izin vereceğinizi belirler. Read Uncommitted, hız için doğruluktan feragat eder; Read Committed, dirty read’i engelleyerek dengeli bir başlangıç sunar; Repeatable Read, aynı satırın işlem boyunca stabil kalmasını sağlar; Serializable ise en güçlü tutarlılık modelini hedefler. MVCC ve snapshot isolation gibi yaklaşımlar, okuma performansını artırırken bazı yarış koşullarını tamamen ortadan kaldırmayabilir.
En sağlıklı yaklaşım, iş akışlarınıza göre hangi anomalilerin kabul edilemez olduğunu belirlemek; kritik transaction’larda daha güçlü seviyeleri kullanırken, genel okuma yükünü gereksiz yere pahalılaştırmamaktır. Böylece hem performansı korur hem de “nadiren olan ama çok can yakan” tutarlılık hatalarını minimize edersiniz.


