İzolasyon Seviyeleri — Aynı İsim, Üç Farklı Davranış
Önce şunu oku: SQL mi, NoSQL mi? — Önce Erişim Desenini Yaz
30 saniyede özet
İki kişi aynı anda aynı hesaptan para çekerse biri kaybolabilir, transaction kullansan bile. Veritabanlarının koruma ayarları (izolasyon seviyeleri) aynı adı taşısa da her motorda farklı davranır.
Aynı hesaptan iki para çekme isteği aynı anda geldi. İkisi de başarılı döndü, ama bakiyeden yalnızca biri düştü. Kod @Transactional idi; ekip “transaction var, eşzamanlılık sorunu olamaz” diye düşünüyordu.
Adım adım oku
- İki para çekme transaction'ı aynı anda başlar ve ikisi de bakiyeyi 100 olarak okur.
- İkisi de kendi okuduğu değerden 50 düşer ve bakiyeye 50 yazar.
- İki çekiş başarılı döndü ama bakiye yalnızca bir kez düştü: kayıp güncelleme.
- SELECT FOR UPDATE ile ilk transaction satırı kilitler; ikincisi bekler, güncel bakiyeyi okur ve doğru sonuç sıfır olur.
-
Bayt: Aynı hesaptan iki para çekildi, ikisi de başarılı, ama bakiyeden yalnızca biri düştü!
-
Sen: Ama kod @Transactional'dı.
-
Bayt: Evet! Transaction varsa eşzamanlılık sorunu olamaz sanıyorduk.
-
Bayt: Ortak alışveriş listesine iki kişi aynı anda bakarsa eve kaç süt gelir?
Aynı anda çalışınca neler bozulur?
izolasyon seviyesiEşzamanlı transaction'ların birbirinin yarım kalmış işlerini ne kadar görebileceğine dair sözleşme: READ UNCOMMITTED'dan SERIALIZABLE'a.Sözlükte gör →, eşzamanlı transaction’ların birbirinin ara hâllerini ne kadar görebileceğine dair bir sözleşmedir. Seviye düştükçe şu durumlara izin verilir:
| Anomali | Ne olur |
|---|---|
| Dirty read | Başkasının commit etmediği değeri okursun |
| Non-repeatable read | Aynı satırı iki kez okursun, farklı değer gelir |
| Lost update | İki iş aynı değeri okuyup üstüne yazar; biri kaybolur |
| Write skew | İki iş farklı satırlara yazar; birlikte bir kuralı bozarlar |
Son ikisi en tehlikelisidir, çünkü hiçbir satır “bozuk” görünmez. Nöbette en az bir doktor kalmalıyken ikisinin de çıkması, bir write skewİki transaction aynı veriyi okuyup farklı satırlara yazar; tek tek doğru olan kararlar birlikte bir kuralı bozar. Snapshot isolation bunu durdurmaz.Sözlükte gör → örneğidir.
Kafam karıştı, daha basit anlat
İzolasyon, aynı anda çalışan işlerin birbirinin yarım hâlini ne kadar görebileceğidir. Seviye düştükçe daha hızlı çalışır, ama daha tuhaf sonuçlar görebilirsin.
Metot `@Transactional`; bakiyeyi okuyup, çekilecek tutarı çıkarıp yazıyor. İki istek aynı anda gelince bir çekim kayboluyor. Transaction neden korumadı?
Her olayı, hangi anomali olduğuna göre ayır.
Aynı isim, farklı davranış
PostgreSQL'de seviyeyi READ UNCOMMITTED yaptın. Başka bir transaction'ın commit etmediği değeri okuyabilir misin? Cevabı göster
Hayır. PostgreSQL bu seviyeyi kabul eder ama READ COMMITTED gibi davranır. Dirty read’i yalnızca SQL Server gösterir; Oracle bu seviyeyi hiç sunmaz.
PostgreSQL ve Oracle MVCCMulti-version concurrency control: yazma yeni bir satır sürümü üretir, okuyan commit edilmiş eski sürümü görür. Okuyan ile yazan birbirini beklemez.Sözlükte gör → kullanır: okuyana satırın commit edilmiş bir sürümünü gösterir, kimseyi bekletmez. SQL Server’ın varsayılanı ise kilittir: okuyan, yazanın işini bitirmesini bekler.
| PostgreSQL | SQL Server | Oracle | |
|---|---|---|---|
| Varsayılan | READ COMMITTED | READ COMMITTED (kilitli) | READ COMMITTED |
| Dirty read | Hiçbir seviyede | Yalnızca READ UNCOMMITTED | Hiçbir seviyede |
| Lost update’i durduran | REPEATABLE READ: hata | REPEATABLE READ: deadlock | SERIALIZABLE: hata |
| Write skew’i durduran | SERIALIZABLE | REPEATABLE READ: deadlock | Hiçbiri |
Kafam karıştı, daha basit anlat
Aynı ayarın adı her veritabanında aynı, ama davranışı farklı olabilir. Kendi veritabanının belgesine bakmadan “bu seviye şunu yapar” deme.
Uzun bir UPDATE transaction'ı açıkken aynı satırları okuyan raporlar SQL Server'da bekliyor, PostgreSQL'de beklemiyor. İkisi de READ COMMITTED. Neden?
Kendin gör
Aynı iki transaction, üç veritabanı, dört seviye
Tohum 877683Oynat ya da adımla.
Şu an ne oldu?
Lost update — PostgreSQL, READ COMMITTED
İki transaction aynı veriye dokunacak. Veritabanı bu seviyede ne yapıyor: izin mi veriyor, bekletiyor mu, reddediyor mu?
Görevler0/3
Commit edilmemiş bir değeri okuaçık
İpucu
Üç motordan yalnızca birinde, en düşük seviyede.
SERIALIZABLE seçiliyken write skew yaşaaçık
İpucu
Snapshot isolation farklı satırlara yazanları durdurmaz.
Lost update senaryosunda hiçbir çekim kaybolmasınaçık
İpucu
READ COMMITTED'ın üstüne çık; bedelini de gör.
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
- Varsayılanla oynat. PostgreSQL, READ COMMITTED: bakiye 50, olması gereken 20.
- Seviyeyi REPEATABLE READ yap. T2 hata aldı; veri korundu.
- Motoru SQL Server yap. Aynı seviye, bu kez deadlock.
- Write skew’e geç, Oracle ve SERIALIZABLE seç. Nöbetçi kalmadı.
- Dirty read’i üç motorda dene.
Simülatörde lost update senaryosu REPEATABLE READ'de PostgreSQL'de serialization hatası, SQL Server'da deadlock verdi. Fark nereden geliyor?
Kaybolan güncellemeyi nasıl önlersin?
Seviyeyi yükseltmek tek yol değildir; çoğu zaman en ucuzu da değildir.
-- 1. Hesabı veritabanına yaptır: oku-hesapla-yaz yokUPDATE accounts SET balance = balance - 30 WHERE id = 1 AND balance >= 30;
-- 2. Okurken kilitleSELECT balance FROM accounts WHERE id = 1 FOR UPDATE;
-- 3. İyimser kilit: sürüm değiştiyse 0 satır güncellenirUPDATE accounts SET balance = 70, version = 8 WHERE id = 1 AND version = 7;JPA’daki @Version üçüncü yolun kendisidir. SQL Server’da FOR UPDATE yerine WITH (UPDLOCK) ipucu kullanılır.
Aynı üç yol, bir bankanın kartla para çekme servisinde de karşına çıkar. Spring tarafında nasıl göründüklerine birlikte bakalım.
Derinleş · Kartla para çekme: kaybolan güncellemeye üç çare 4 dosya · ~73 satır · ilk okumada atlayabilirsin
Kafam karıştı, daha basit anlat
Güncellemenin kaybolmaması için her zaman seviyeyi yükseltmen gerekmez. Çoğu zaman hesabı doğrudan veritabanına yaptırmak (SET stok = stok - 1) yeter.
Bu metot yük altında bazen para kaybettiriyor. Hatalı satır hangisi?
Stok düşümünde lost update var. İzolasyon seviyesini değiştirmeden en sade düzeltme hangisi?
Tuzaklar
@Transactional seviye seçmez. Belirtmezsen veritabanının varsayılanı geçerlidir; üçünde de READ COMMITTED.
WITH (NOLOCK). SQL Server’da READ UNCOMMITTED demektir: commit edilmemiş, hatta iki kez okunmuş satırlar gelebilir.
RCSI açık mı? SQL Server’da READ_COMMITTED_SNAPSHOT açıksa okuyucular beklemez; Azure SQL Database’de varsayılan olarak açıktır. Aynı kod iki ortamda farklı davranır.
Yeniden denemeyen yüksek izolasyon. Serialization hatası ya da deadlock kurbanı bir hata değil, “tekrar dene” demektir. Tekrar deneme yoksa kullanıcı 500 görür.
SQL Server'daki raporlar yavaş diye her sorguya `WITH (NOLOCK)` eklendi. Ne riske girildi?
Kendini sına
Transaction kullanmak her eşzamanlılık sorununu kendiliğinden önler.
Oracle'da nöbet kuralı (en az bir doktor nöbette) SERIALIZABLE seviyede korunuyor sanılıyordu, ama iki doktor aynı anda çıkabildi. Neden?
Aklında kalacak üç şey
- 1 Transaction kullanmak, aynı anda çalışan işlerin birbirini bozmayacağı anlamına gelmez. Üç motorun varsayılanında da oku-hesapla-yaz kalıbında bir güncelleme kaybolur.
- 2 Seviyenin adı değil, motorun o seviyedeki davranışı önemlidir: SQL Server kilitle bekletir, PostgreSQL ve Oracle eski sürümü gösterir.
- 3 Daha sıkı koruma, yanlış sonucu bir hataya çevirir. Uygulama o hatada işi yeniden denemeyi bilmelidir.
5 kart sonraki derste seni bekliyor