İçeriğe geç
academia.sh

Ders 10 / 20

Yalıtım Düzeyleri

Kirli okuma, tekrarlanamayan okuma ve hayalet satır anomalileri, standartta tanımlı dört yalıtım düzeyi, iki bağlantıyla gerçek bir gözlem ve gösterilemeyen düzeyin model üzerinden anlatımı.

İçindekiler

Önceki iki ders boyunca veritabanına yalnız bir bağlantı erişiyordu. İşlem kendi değişikliklerini görüyor, dışarıda bir şey olmuyordu. Gerçek sistemlerde ise aynı anda birçok bağlantı okur ve yazar; o zaman iki soru anlam kazanır: bir işlem, başka bir işlemin henüz kesinleştirmediği değişikliği görebilir mi? Aynı sorgu bir işlem içinde iki kez çalıştırılırsa aynı sonucu verir mi?

Yalıtım (isolation), bu soruların yanıtını belirleyen özelliktir. İlişkisel Kuram kursundaki ACID sıralamasının üçüncü harfi budur. Tam yalıtım, eşzamanlı çalışan işlemlerin sonucunun, onları sırayla çalıştırmakla aynı olmasını ister. Bu güçlü garanti pahalıdır; bu yüzden standart, gevşetilmiş düzeyler tanımlar ve her düzeyi hangi anomaliye izin verdiğiyle tarif eder.

Üç Anomali

Standart üç okuma anomalisi tanımlar. Üçü de “iki işlem araya girdiğinde okunan verinin bozulması” ile ilgilidir, ancak bozulmanın biçimi farklıdır.

Kirli okuma (dirty read). Bir işlem, başka bir işlemin henüz kesinleştirmediği değişikliği okur. Diğer işlem geri alınırsa, okunan değer hiç var olmamış bir değerdir.

Tekrarlanamayan okuma (non-repeatable read). Bir işlem aynı satırı iki kez okur ve farklı değerler görür; arada başka bir işlem o satırı güncelleyip kesinleştirmiştir.

Hayalet satır (phantom row). Bir işlem aynı koşulu iki kez sorgular ve ikincisinde koşula uyan yeni satırlar görür; arada başka bir işlem satır eklemiştir. Tekrarlanamayan okumadan farkı, değişenin bir satırın değeri değil, kümenin üyeliği olmasıdır.

Ayrım pratikte önemlidir, çünkü satır kilitleriyle önlenebilen ile önlenemeyen buradan ayrılır: var olan bir satır kilitlenebilir, henüz var olmayan bir satır kilitlenemez.

Dört Düzey

Standart dört düzey tanımlar. Her düzey, kendinden gevşek olanların önlediklerini de önler:

Düzey Kirli okuma Tekrarlanamayan okuma Hayalet satır
READ UNCOMMITTED olabilir olabilir olabilir
READ COMMITTED olamaz olabilir olabilir
REPEATABLE READ olamaz olamaz olabilir
SERIALIZABLE olamaz olamaz olamaz

Tabloyu okurken iki nokta gözden kaçmamalıdır.

Birincisi, tablo izin verilenleri sıralar, olacakları değil. READ COMMITTED düzeyinde tekrarlanamayan okuma “olabilir”; her zaman olur demek değildir. Bir motor, daha güçlü bir garanti sunup yine de o düzeyin adını kullanabilir.

İkincisi, düzeyler bir gerçekleştirim değil, bir sözleşmedir. Aynı adı taşıyan iki motor bunu farklı biçimlerde sağlayabilir: biri kilitleme, diğeri çok sürümlü eşzamanlılık denetimi kullanabilir. Sonuç, yasaklanan anomalilerin ortaya çıkmamasıdır; yol serbesttir.

Düzey, standartta SET TRANSACTION ISOLATION LEVEL deyimiyle seçilir. Hangi düzeylerin gerçekten sunulduğu motora göre değişir: bazı motorlar dördünü de kabul eder ama bir kısmını daha güçlü bir düzeye eşler, bazıları hiç seçim sunmaz. Aşağıdaki gözlemin yapıldığı motor ikinci gruptadır — düzey seçimi yoktur, okumalar tutarlı bir görüntü üzerinden yapılır.

İki Bağlantıyla Gözlem

Aşağıdaki betik aynı veritabanı dosyasına iki ayrı bağlantı açar ve araya girmeyi denetimli biçimde üretir. Önce okuyucu bir işlem açmadan iki kez okur; sonra aynı okumayı açık bir işlem içinde yapar. Yazar her iki durumda da aynı işi yapar: bir satırı günceller ve yeni bir açık ödünç kaydı ekler.

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

const okuyucu = new DatabaseSync('yalitim.db');
const yazar = new DatabaseSync('yalitim.db');

okuyucu.exec('PRAGMA journal_mode = WAL');
yazar.exec(`
  CREATE TABLE odunc(id INTEGER PRIMARY KEY, kitap_id INT, uye_id INT, alis TEXT,
                     iade TEXT);
  INSERT INTO odunc VALUES (1,1,4,'2024-03-01',NULL),(2,3,4,'2024-03-04',NULL),
                           (3,5,7,'2024-03-06',NULL);
`);

const oku = (b) => b.prepare(
  `SELECT (SELECT alis FROM odunc WHERE id = 1) AS kayit1_alis,
          (SELECT COUNT(*) FROM odunc WHERE iade IS NULL) AS acik_kayit`).get();

console.log('— işlem açmadan okuma —');
console.log('1. okuma:', JSON.stringify(oku(okuyucu)));
yazar.exec("UPDATE odunc SET alis = '2024-03-02' WHERE id = 1");
yazar.exec("INSERT INTO odunc VALUES (4,7,4,'2024-03-11',NULL)");
console.log('yazar bir satırı güncelledi, bir satır ekledi, kesinleştirdi');
console.log('2. okuma:', JSON.stringify(oku(okuyucu)));

console.log('— işlem içinde okuma —');
okuyucu.exec('BEGIN');
console.log('1. okuma:', JSON.stringify(oku(okuyucu)));
yazar.exec("UPDATE odunc SET alis = '2024-03-03' WHERE id = 1");
yazar.exec("INSERT INTO odunc VALUES (5,9,7,'2024-03-13',NULL)");
console.log('yazar aynı işi bir kez daha yaptı ve kesinleştirdi');
console.log('2. okuma:', JSON.stringify(oku(okuyucu)));
okuyucu.exec('COMMIT');
console.log('işlem bitti, 3. okuma:', JSON.stringify(oku(okuyucu)));
JS
node yalitim.mjs
rm -f yalitim.db yalitim.db-wal yalitim.db-shm
— işlem açmadan okuma —
1. okuma: {"kayit1_alis":"2024-03-01","acik_kayit":3}
yazar bir satırı güncelledi, bir satır ekledi, kesinleştirdi
2. okuma: {"kayit1_alis":"2024-03-02","acik_kayit":4}
— işlem içinde okuma —
1. okuma: {"kayit1_alis":"2024-03-02","acik_kayit":4}
yazar aynı işi bir kez daha yaptı ve kesinleştirdi
2. okuma: {"kayit1_alis":"2024-03-02","acik_kayit":4}
işlem bitti, 3. okuma: {"kayit1_alis":"2024-03-03","acik_kayit":5}

Birinci bölümde okuyucu işlem açmadı; her SELECT kendi başına bir işlemdi. İki okuma arasında hem satırın değeri değişti — tekrarlanamayan okumanın tanımı — hem de koşula uyan satır sayısı arttı — hayalet satırın tanımı. Bu bir hata değildir: iki ayrı işlem arasında dünyanın değişmesi beklenen şeydir.

İkinci bölümde okuyucu BEGIN ile bir işlem açtı. Yazar aynı iki değişikliği yaptı ve kesinleştirdi, ancak okuyucunun ikinci okuması hiç değişmedi: ne değer ne sayım. İşlem kapandıktan sonra yapılan üçüncü okuma yeni durumu gösterdi. Aradaki fark, işlemin ilk okumada aldığı tutarlı görüntüyü sonuna kadar koruması, yani anlık görüntü üzerinden okumaya devam etmesidir.

Gözlem, tablonun ilk üç satırının farkını somutlaştırır. İşlem açmadan okuyan taraf, READ COMMITTED düzeyinin izin verdiği davranışı gösterdi: yalnız kesinleşmiş veriyi gördü, ama tekrarlanan okumaları farklıydı. İşlem içinde okuyan taraf, SERIALIZABLE düzeyinin okuma tarafındaki davranışını gösterdi: hem tekrarlanamayan okuma hem hayalet satır ortadan kalktı.

Gösterilemeyen Düzey

Tablodaki dört düzeyden biri bu motorda gözlenemez. READ UNCOMMITTED, kesinleşmemiş değişikliğin okunmasına izin verir; kullanılan motor bunu hiçbir ayarla sunmaz, çünkü okumalar zaten kesinleşmiş bir anlık görüntüden yapılır. Kirli okumanın sonucunun ne olduğunu göstermek için gerçek bir motor çıktısı üretilemez.

Bunun yerine davranış modellenebilir. Aşağıdaki betik bir veritabanı değildir; tek bir kaydın kesinleşmiş ve kesinleşmemiş değerlerini tutan küçük bir zamanlama modelidir. Kirli okumaya izin veren ve vermeyen iki durumda okuyucunun ne göreceğini karşılaştırır:

cat > kirli-model.mjs <<'JS'
// Kirli okuma modeli: kesinleşmemiş değeri gören bir motorun davranışını taklit
// eder. Gerçek bir motor çıktısı değildir; amaç sonucun ne olduğunu göstermektir.
const kayit = { rafta: 1, kesinlesmemis: null };

const oku = (kirliOkumaVar) =>
  kirliOkumaVar && kayit.kesinlesmemis !== null ? kayit.kesinlesmemis : kayit.rafta;

const dene = (kirliOkumaVar) => {
  kayit.kesinlesmemis = 0;              // yazar: kitabı ödünç verdi, henüz kesinleşmedi
  const gorulen = oku(kirliOkumaVar);   // okuyucu araya girdi
  kayit.kesinlesmemis = null;           // yazar: işlemi geri aldı
  return { okuyucununGordugu: gorulen, geriAlmaSonrasiGercek: kayit.rafta };
};

console.log('kirli okumaya izin veren düzey    :', JSON.stringify(dene(true)));
console.log('kirli okumaya izin vermeyen düzey :', JSON.stringify(dene(false)));
JS
node kirli-model.mjs
kirli okumaya izin veren düzey    : {"okuyucununGordugu":0,"geriAlmaSonrasiGercek":1}
kirli okumaya izin vermeyen düzey : {"okuyucununGordugu":1,"geriAlmaSonrasiGercek":1}

Birinci satırda okuyucu kitabı ödünç verilmiş gördü; yazar işlemi geri aldığı için o değer hiçbir zaman gerçek olmadı. Kirli okumanın kötülüğü budur: eski bir değer değil, hiç var olmamış bir değer okunur. Buna dayanarak verilen bir karar — kitabı rezerve etmemek gibi — hiçbir gerçek duruma karşılık gelmez.

Bu blok bir model olduğu için, çıktısı bir motorun davranışı hakkında kanıt değildir; yalnız tanımın sonucunu görünür kılar. Bir motorda READ UNCOMMITTED gerçekten sunuluyorsa, aynı sonucun o motorda ölçülmesi gerekir.

Düzey Seçmek

Düşük düzeyler daha az koordinasyon, dolayısıyla daha yüksek eşzamanlılık sağlar. Bedeli, uygulamanın anomalilerle kendi başına baş etmesidir.

Karar, işin okuduğu veriye ne yaptığına bakılarak verilir. Okunan değer yalnız görüntüleniyorsa gevşek bir düzey yeterlidir; okunan değere dayanarak yazma yapılıyorsa — “rafta mı diye bak, raftaysa ödünç ver” — gevşek düzey sessiz bir hataya kapı açar. İki işlem aynı anda rafta görüp aynı kitabı iki üyeye verebilir.

Bu duruma kayıp güncelleme denir ve yalnız yalıtım düzeyi yükseltilerek değil, okumayı kilitleyerek de önlenebilir. İki yolun da bedeli, aynı satıra dokunan işlemlerin birbirini beklemesidir. Sonraki dersin konusu bu bekleme mekanizmasıdır.

Özet

  • Kirli okuma kesinleşmemiş veriyi, tekrarlanamayan okuma değişmiş bir satır değerini, hayalet satır koşula uyan yeni satırları görür.
  • Standart dört düzeyi hangi anomaliye izin verdikleriyle tanımlar; tablo izin verilenleri sıralar, olacakları değil.
  • Düzey bir sözleşmedir, gerçekleştirim değil; aynı adı taşıyan iki motor bunu kilitleme ya da çok sürümlü denetimle sağlayabilir.
  • İki bağlantılı gözlemde işlem açmadan okuyan taraf hem değer hem satır sayısı değişikliğini gördü; açık işlem içinde okuyan taraf ilk okumadaki görüntüyü sonuna kadar korudu.
  • Okunan değere dayanarak yazma yapılıyorsa gevşek düzey kayıp güncellemeye kapı açar.

Sonraki Adım

Yalıtım düzeyleri neyin görünüp neyin görünmeyeceğini söyler, ama bunun bedelini söylemez. İki işlem aynı satıra yazmak istediğinde biri beklemek zorundadır; beklemenin ne kadar süreceği, neyin kilitlendiğine bağlıdır. İki işlem birbirinin tuttuğu kaynağı beklerse hiçbiri ilerleyemez. Sonraki ders kilit türlerini, gerçek bir çakışmada motorun verdiği hatayı ve karşılıklı beklemenin nasıl çözüldüğünü ele alacak.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat