15 puan yazan xguru 2024-07-31 | Henüz yorum yok. | WhatsApp'ta paylaş
  • Son birkaç yılda SQLite, sunucu süreçlerinin arka ucunda kullanılan güçlü, süreç içi ve yüksek güvenilirlikli bir SQL veritabanı motoru olarak öne çıktı
  • SQLite geliştiricileri, geleneksel istemci veya edge uygulaması rolü dışında bu amaçla kullanılmasını neredeyse açıkça caydırmasına rağmen, popülaritesi hızla arttı

SQLite ile ilgilenmeye başlamamın başlıca nedenleri:

  • Kavramsal olarak basit: birincil anahtara göre bölümlenmiş satır/tuple'ların bir B-tree yapısını düşünün. Bunu diskte güvenli biçimde kalıcı tutmak için kapsamlı şekilde test edilmiş ve üzerine bir SQL etkileşim katmanı eklenmiş
  • Litestream ile pratik bir yedekleme stratejisi kurulabiliyor. WAL uzak bir konuma yedekleniyor ve sürekli replike ediliyor. Yedekler, başlangıçta basit bir komutla otomatik geri yüklenebiliyor
  • Hâlâ çevrimdışı çalışabilen tam bir geliştirme ortamını seviyorum
  • file::memory: ile bellek içi çalışma mümkün; böylece test kodu gerektiğinde instance'ı kolayca başlatıp kapatabiliyor

Tek writer sınırlaması

  • SQLite geliştiricileri tarafından "sunucuda SQLite sınırlamaları" iyi belgelenmiş durumda ve en iyi sunucu tarafı ayarları incelenmiş. Ancak öne çıkan sınırlama, yüksek trafikli web siteleri; yani yazma ağırlıklı siteler
  • WAL modunda SQLite, tasarım gereği tek bir writer kullanır. Bu da aynı anda en fazla 1 yazma transaction'ına ve birden çok salt okunur transaction'a izin verir
  • Bu tasarım, yazma yoğun ve yüksek trafikli web sitelerinde darboğazı o tek writer'ın throughput'unu yönetmeye indirger. Bu da bizi modern teknolojinin temel yapı taşlarından birine geri götürür

SQLite

  • SQLite varsayılan olarak katı SERIALIZABLE isolated transaction'lar sunar. Bu, isolation garantilerinin en güçlü seviyesidir
  • Tek writer kullanarak SQLite, yazma transaction'ı sürerken alttaki verinin değişmediğini kolayca garanti edebilen bir pessimistic concurrency control biçimi kullanır

Postgres

  • Postgres, SQL standardında tanımlanan varsayılan SERIALIZABLE düzeyini fiilen kullanmaz; bunun yerine daha gevşek olan READ COMMITTED seviyesini seçer (çok daha karmaşık multiversion concurrency control'a rağmen)
    • Bu gevşeklik, non-repeatable read riskini doğurur. Yani aynı transaction içinde, arka planda başka COMMITTED transaction'lar tarafından değer değişirse, aynı okuma sorgusunu birden fazla çalıştırdığınızda farklı sonuçlar alabilirsiniz
    • Postgres bu isolation seviyesini seçerek, transaction'ların eski veri üzerinde çalışması riskini açık bırakır. Geliştiricilerin bunu akılda tutması gerekir
  • SERIALIZABLE olarak ayarlandığında Postgres, transaction sırasında erişilen veriyi izlemek ve commit'ten önce değişmediğini doğrulamak için optimistic concurrency control şeması kullanır
    • Postgres bunu, bellek kullanımını yönetmek için transaction'a bağlı olarak satır düzeyi veya sayfa düzeyi kilitler üzerinden yapar
    • Bu desen optimistic olarak adlandırılır; çünkü transaction commit edildiğinde, transaction'ın izlediği veri ne kadar ayrıntılıysa, alttaki verinin değişmemiş olması o kadar olası kabul edilir

FoundationDB

  • Transaction'lar yalnızca ilişkisel veritabanlarıyla sınırlı değildir. Dağıtık key-value store'larda SERIALIZABLE garantisini elde etmek için optimistic concurrency control kullanılır
  • NoSQL ilk ortaya çıktığında, ACID garantili dağıtık NoSQL store'lar yaygın değildi. FoundationDB bir transaction manifesto'su yazarak geliştiricilerin ACID garantilerinden büyük fayda sağlayabileceğini vurguladı
  • FoundationDB, optimistic concurrency control için kodun nasıl yazılacağına dair yönlendirme sunar ve bazen eşzamanlı transaction çakışmaları nedeniyle verinin değişmesi sonucunda transaction'ın otomatik olarak yeniden deneneceğini belirtir

Idempotence

  • İdempotent transaction, bir kez commit edildiğinde de iki kez commit edildiğinde de aynı etkiyi üreten transaction'dır
  • FoundationDB, transaction'ları idempotent hale getirmek için, çakışmalar nedeniyle transaction'ın birden çok kez yeniden denenmesi durumunda sorunları önleyen kalıplar sunar

Peki tüm bunları göz önünde bulundurduğumuzda, SQLite hangi seçenekleri sunuyor?

BEGIN …

SQLite, geliştiricilerin transaction'ın nasıl davranacağını motora belirtmesini sağlayan çeşitli yollar sunar; WAL modunda bunlar pratikte DEFERRED ve IMMEDIATE'a indirgenebilen IMMEDIATE, EXCLUSIVE ve DEFERRED anahtar sözcükleridir

DEFERRED

  • Transaction, diğer okuma veya yazma transaction'larıyla eşzamanlı çalışabilen bir READ modunda başlar
  • Ancak veritabanı durumunu değiştiren bir sorgu (INSERT, UPDATE, DELETE) çalıştırıldığında engelleyici READ-WRITE transaction'a yükseltilir
  • Yükseltme sırasında veritabanı başka bir transaction tarafından kilitliyse SQLITE_BUSY hatası döner. Bunu istemci ele almalıdır

IMMEDIATE

  • Transaction hemen READ-WRITE modunda başlar
  • Halihazırda çalışan bir yazma transaction'ı varsa anında SQLITE_BUSY döner
  • Bunun nasıl ele alınacağına istemci karar vermelidir

CONCURRENT

  • SQLite'ta transaction'ları pessimistic yaklaşımdan sınırlı bir optimistic yaklaşıma taşıyan deneysel bir dal bulunuyor
  • Sınırlı denmesinin nedeni, optimistic locking'in satır/tuple düzeyinde değil veritabanı sayfası düzeyinde (varsayılan 4096 bytes) çalışmasıdır
  • CONCURRENT modunda SQLite, aynı anda birden çok yazma transaction'ını etkin tutabilir; ancak commit'ten önce transaction boyunca erişilen sayfaların, transaction başladığından beri değişmediğini doğrular
  • Çakışma olmazsa değişiklikler sıralı biçimde commit edilir ve katı SERIALIZABLE garantisi sağlanır. Çakışma olursa SQLITE_BUSY döner

HC-Tree

  • SQLite'ın bir başka deneysel dalı olan [HC-Tree], optimistic satır/tuple düzeyi kilitleme sunmayı hedefleyen devam eden bir çalışmadır. İlginç sonuçlardan biri de, böyle bir tasarımın performans avantajlarını BEGIN CONCURRENT dalıyla karşılaştıran güçlü bir benchmark seti sunmasıdır

Onların benchmark yaklaşımını alıp standart seçenekler üzerinde çalıştırmak nasıl olurdu?

Benchmark

nUpdate=1, nScan=0

  • Bu yalnızca yazma yapan transaction, IMMEDIATE ile DEFERRED arasındaki avantajı açıkça gösteriyor. Locking hemen gerçekleşiyor ve transaction, yükseltme maliyetinden etkilenmiyor
  • CONCURRENT, thread sayısı arttıkça ve çakışmalar çoğaldıkça throughput artışı gösteriyor

nUpdate=10, nScan=0

  • Beklendiği gibi, write batching 16 thread'de güncellenen satır sayısına ciddi katkı sağlıyor. CONCURRENT, ~12k/sn'den ~19k/sn'ye çıkıyor
  • IMMEDIATE ile DEFERRED arasındaki fark daha az önemli hale geliyor. Çünkü transaction yükseltme maliyetinden ziyade güncellemenin kendi maliyeti baskın hale geliyor

nUpdate=1, nScan=10

  • Bu transaction, rastgele okumalar nedeniyle sayfa düzeyi CONCURRENT locking'in zayıflığını ortaya çıkarmalı
  • Güncelleme yapacak transaction'larda neden DEFERRED yükseltme maliyetine kıyasla IMMEDIATE kullanmanın önemli olduğunu hemen gösteriyor
  • CONCURRENT içinse, alttaki çakışmalar aslında çok artmadığı için bu sonuçlar oldukça sağlam görünüyor

nUpdate=0, nScan=10

  • Bu salt okunur batch transaction, pessimistic concurrency control'ün etkisini gösteriyor
  • Neden tüm transaction'larda varsayılan olarak IMMEDIATE ayarlanmaması gerektiğini gösteriyor
  • CONCURRENT ile IMMEDIATE karşılaştırması, CONCURRENT modunu kullanmanın küçük bir dezavantajı olduğunu gösteriyor. "Tüm durumlarda performans biraz daha düşük"
    • Yine de CONCURRENT iyi bir varsayılan seçenek olabilir

Henüz yorum yok.

Henüz yorum yok.