İçeriğe geç
academia.sh

Ders 16 / 19

Yerel Veritabanı

Birincil anahtarlı varsa güncelle yoksa ekle işleminin sağlam, kendiliğinden artan anahtarla eklemenin duyarlı olduğunun ve okuma sırasının sonucu değiştirdiğinin ölçümü.

Yayın Güncelleme

Bu derste

  • Dosya yerine yerel veritabanının ne zaman gerektiğini ayırır
  • Birincil anahtarlı varsa güncelle yoksa ekle işleminin sağlam olduğunu ölçer
  • Kendiliğinden artan anahtarla eklemenin yinelemeye duyarlı olduğunu doğrular
  • Okuma sırasının sıralı ya da ekleme sırasıyla yapılmasının sınıfı değiştirdiğini gösterir
İçindekiler

Dosyalar veriyi bir bayt dizisi olarak tutar; içinde bir kaydı bulmak, bir alanı güncellemek ya da iki kaydı birleştirmek uygulamanın işidir. Veri satırlara ve alanlara bölünebiliyorsa, aranıyor ve güncelleniyorsa yerel veritabanı daha uygun bir araçtır. Aygıttaki en yaygın seçenek gömülü bir ilişkisel veritabanıdır; tek bir dosyada yaşar ve sorgu dili sunar. Bu dersin sorusu önceki derslerle aynıdır: bu işlem yinelenirse ya da sırası değişirse sonuç değişir mi? Fark şudur: veritabanı kimliği kendisi yönetir ve bu kimliğin nereden geldiği sonucu belirler.

// Replay.swift — bir islemin yinelemeye ve siraya duyarliligini siniflandirir
struct Trial<State: Equatable & CustomStringConvertible> {
    let label: String
    let once: State
    let twice: State
    let forward: State
    let backward: State

    var sensitivity: String {
        switch (once != twice, forward != backward) {
        case (false, false): return "saglam"
        case (true, false): return "yinelemeye-duyarli"
        case (false, true): return "siraya-duyarli"
        case (true, true): return "cift-duyarli"
        }
    }

    func report() -> String {
        print("\(label) | bir=\(once) iki=\(twice) ab=\(forward) ba=\(backward) -> \(sensitivity)")
        return sensitivity
    }
}

func replay<State>(_ label: String, from start: State, _ a: (State) -> State, then b: (State) -> State) -> Trial<State> {
    Trial(label: label, once: a(start), twice: a(a(start)), forward: b(a(start)), backward: a(b(start)))
}

func countSensitivities(_ results: [String]) {
    for name in ["saglam", "yinelemeye-duyarli", "siraya-duyarli", "cift-duyarli"] {
        print("\(name): \(results.filter { $0 == name }.count)")
    }
}

bir işlemin başlangıç durumuna bir kez, iki iki kez uygulanmasıdır; ab önce a sonra b, ba ise tersi sıradır. Aşağıdaki model bir tablonun saf Swift karşılığıdır. Model sorgu motoru değildir; yalnız işlemin tabloda bıraktığı sonucu kurar.

Birincil Anahtar ve Ekleme

Her satırın bir birincil anahtarı vardır ve tabloda aynı anahtardan iki satır olamaz. Bir satır eklenirken anahtar iki yoldan gelir. Uygulama anahtarı kendisi verir; bu durumda aynı anahtarla gelen ikinci ekleme ya hata verir ya da varsa güncelle yoksa ekle sözdizimiyle mevcut satırı değiştirir. Ya da veritabanı anahtarı kendiliğinden üretir; bu durumda her ekleme yeni bir satır açar.

import SQLite3

var database: OpaquePointer?
sqlite3_open("notes.sqlite", &database)

sqlite3_exec(database, "CREATE TABLE IF NOT EXISTS notes (id TEXT PRIMARY KEY, body TEXT)", nil, nil, nil)
sqlite3_exec(database, "INSERT INTO notes (id, body) VALUES ('k1', 'a1') ON CONFLICT(id) DO UPDATE SET body = excluded.body", nil, nil, nil)
sqlite3_exec(database, "SELECT id, body FROM notes ORDER BY id", nil, nil, nil)
sqlite3_close(database)

Bu parça KAYNAK’tır; bir veritabanı dosyası gerektirdiği için burada koşmaz. ON CONFLICT(id) DO UPDATE yan tümcesi varsa güncelle yoksa ekle davranışını kurar: anahtar yoksa satırı ekler, varsa body alanını yenisiyle değiştirir. ORDER BY id okumayı anahtara göre sıralar. Bu yan tümce olmadan satırların hangi sırayla döneceği sorgu motoruna bırakılır.

Tablonun Modeli

Model, anahtarların ekleme sırasını ve anahtardan değere bir eşlemeyi tutar. İki ek bilgi taşır: sıradaki kendiliğinden artan anahtar ve okumanın anahtara göre sıralı olup olmadığı.

// TableModel.swift — bir tablonun saf Swift modeli
struct TableBox: Equatable, CustomStringConvertible {
    private(set) var keys: [String] = []
    private(set) var values: [String: String] = [:]
    private(set) var nextId = 1
    let sortedRead: Bool

    init(sortedRead: Bool = true) {
        self.sortedRead = sortedRead
    }

    var description: String {
        let order = sortedRead ? keys.sorted() : keys
        return order.isEmpty ? "-" : order.map { "\($0)=\(values[$0] ?? "-")" }.joined(separator: ",")
    }

    static func == (lhs: TableBox, rhs: TableBox) -> Bool {
        lhs.description == rhs.description
    }

    mutating func upsert(_ value: String, key: String) {
        if values[key] == nil { keys.append(key) }
        values[key] = value
    }

    mutating func insert(_ value: String) {
        let key = String(nextId)
        nextId += 1
        keys.append(key)
        values[key] = value
    }
}

func upserting(_ value: String, key: String) -> (TableBox) -> TableBox {
    { table in
        var next = table
        next.upsert(value, key: key)
        return next
    }
}

func inserting(_ value: String) -> (TableBox) -> TableBox {
    { table in
        var next = table
        next.insert(value)
        return next
    }
}

Mevcut bir anahtara upsert, satırı yerinde bırakıp yalnız değeri değiştirir; ekleme sırasındaki yeri korunur. Kendiliğinden artan anahtar 1’den başlar ve her eklemede bir artar.

KL9. Model, sıralama belirtilmediğinde satırların ekleme sırasıyla döneceğini varsayar. Gerçek sorgu motoru bu sırayı garanti etmez; sınırlayıcı ölçüm bu varsayımı açıkça kullanır.

KL10. Model tek bir tabloyu ve tek bir birincil anahtar sütununu kurar. Birden çok sütunlu anahtar ve ilişkiler kurulmadı.

Dört İşlemin Ölçümü

Dört işlem kurulur. Birincisi iki farklı anahtara varsa güncelle yoksa ekle uygulamaktır. İkincisi aynı anahtara iki farklı değer yazmaktır. Üçüncüsü aynı değeri kendiliğinden artan anahtarla iki kez eklemektir. Dördüncüsü iki farklı değeri aynı yolla eklemektir.

// main.swift — birincil anahtar ve kendiliginden artan anahtar
let distinctKeys = replay("varsa-guncelle-ayri-anahtar", from: TableBox(), upserting("a1", key: "k1"), then: upserting("b2", key: "k2")).report()
let sameKey = replay("varsa-guncelle-ayni-anahtar", from: TableBox(), upserting("a1", key: "k1"), then: upserting("b2", key: "k1")).report()
let autoSame = replay("otomatik-ayni-deger", from: TableBox(), inserting("a1"), then: inserting("a1")).report()
let autoTwo = replay("otomatik-iki-deger", from: TableBox(), inserting("a1"), then: inserting("b2")).report()

countSensitivities([distinctKeys, sameKey, autoSame, autoTwo])
varsa-guncelle-ayri-anahtar | bir=k1=a1 iki=k1=a1 ab=k1=a1,k2=b2 ba=k1=a1,k2=b2 -> saglam
varsa-guncelle-ayni-anahtar | bir=k1=a1 iki=k1=a1 ab=k1=b2 ba=k1=a1 -> siraya-duyarli
otomatik-ayni-deger | bir=1=a1 iki=1=a1,2=a1 ab=1=a1,2=a1 ba=1=a1,2=a1 -> yinelemeye-duyarli
otomatik-iki-deger | bir=1=a1 iki=1=a1,2=a1 ab=1=a1,2=b2 ba=1=b2,2=a1 -> cift-duyarli
saglam: 1
yinelemeye-duyarli: 1
siraya-duyarli: 1
cift-duyarli: 1

İlk satırda iki farklı anahtar hangi sırayla yazılırsa yazılsın aynı tabloyu verir ve aynı yazmayı yinelemek tabloyu değiştirmez. Okuma anahtara göre sıralandığı için ekleme sırası da görünmez. Sınıf sağlamdır. Bu, bu derste ölçülen tek sağlam satırdır ve nedeni iki şeyin birlikte bulunmasıdır: anahtarı uygulama verir ve okuma sıralıdır.

İkinci satırda aynı anahtara iki farklı değer yazılıyor. Yineleme zararsızdır; ama ab b2, ba a1. Son yazan kazanır. Birincil anahtar çift satırı engeller, çakışmada kimin kazanacağını söylemez. Sınıf sıraya duyarlıdır.

Üçüncü satırda anahtarı veritabanı üretir. Aynı değeri iki kez eklemek iki satır bırakır: 1=a1 ve 1=a1,2=a1. İki işlem aynı olduğundan sıra anlamsızdır. Sınıf yinelemeye duyarlıdır. Yeniden denenen bir ekleme çağrısı, veritabanı bunu iki ayrı kayıt sayacağı için çiftlenir.

Dördüncü satırda iki farklı değer iki farklı sırayla eklenince anahtarlar da değerlere farklı dağılır: 1=a1,2=b2 ve 1=b2,2=a1. Sınıf çift duyarlıdır.

Sayım her sınıftan birer tanedir.

Anahtarı Kim Verir

Ölçümün söylediği pratik bir kural olur. Kayıtların doğal bir kimliği varsa, örneğin sunucunun verdiği bir kimlik ya da içerikten türeyen bir özet, anahtarı uygulama vermelidir ve ekleme varsa güncelle yoksa ekle olarak yazılmalıdır. Böylece yeniden deneme güvenli hâle gelir. Doğal bir kimlik yoksa kendiliğinden artan anahtar kullanılır; ama yeniden deneme çiftlenir ve bunu önleyen bir şey, örneğin ayrı bir benzersiz sütun, tasarıma eklenir.

Sık Yapılan Hatalar

  • Yeniden denenen bir ekleme çağrısında kendiliğinden artan anahtara güvenmek. Satır çiftlenir.
  • Sıralama belirtmeden okumak ve ekleme sırasının korunduğunu sanmak.
  • Varsa güncelle yoksa ekle yerine önce okuyup sonra ekleyen iki adımlı bir yol yazmak. İki adım arasında başka bir yazma araya girebilir.
  • Aynı satırı iki kaynaktan güncelleyip kazananın belli olduğunu sanmak. Son yazan kazanır.
  • Her küçük değer için tablo açmak. Tercih deposu bunun için yeterlidir.
  • Çok sayıda satırı tek tek, ayrı işlemlerle yazmak. Hepsi tek bir işlemde yazılırsa tutarlılık ve hız kazanılır.

KL11. Model, her çağrının kesintisiz ve tek bir bağlantıdan koştuğunu varsayar. İki bağlantının aynı anda yazdığı durum kurulmadı.

Sınırlayıcı Ölçüm

İlk satırın sağlamlığı okuma sırasına dayanıyordu. Aynı iki işlemi, okumayı ekleme sırasıyla yapan bir tabloda ölçelim ve karşılaştırma için sıralı olanla yan yana koyalım.

// main.swift — okuma sirasi
let sorted = replay("anahtar-sirasi-okuma", from: TableBox(sortedRead: true), upserting("a1", key: "k1"), then: upserting("b2", key: "k2")).report()
let insertion = replay("ekleme-sirasi-okuma", from: TableBox(sortedRead: false), upserting("a1", key: "k1"), then: upserting("b2", key: "k2")).report()

countSensitivities([sorted, insertion])
anahtar-sirasi-okuma | bir=k1=a1 iki=k1=a1 ab=k1=a1,k2=b2 ba=k1=a1,k2=b2 -> saglam
ekleme-sirasi-okuma | bir=k1=a1 iki=k1=a1 ab=k1=a1,k2=b2 ba=k2=b2,k1=a1 -> siraya-duyarli
saglam: 1
yinelemeye-duyarli: 0
siraya-duyarli: 1
cift-duyarli: 0

İşlem ve veri aynıdır; yalnız okuma değişti. Anahtara göre sıralı okuma sağlam, ekleme sırasıyla okuma sıraya duyarlıdır: k1,k2 ve k2,k1. Tablonun içeriği iki durumda da aynı iki satırdır; fark satırların nasıl listelendiğidir. Bu, bir kümenin sıralı bir dizi gibi okunduğu her yerde görülen bir hatadır: sonuç kümeyi değil, listelemeyi etkiler.

Sınırın iki yönü vardır. Birincisi KL9’un kullanıldığı yerdir: ekleme sırasıyla okuma gerçek sorgu motorunda garanti değildir, burada yalnız modelin bir seçeneğidir. İkincisi kuralın görmediği şeydir: kural iki durumun eşit olmadığını söyler, farkın okuma sırasından geldiğini söylemez. Nedeni insan koyar.

Gerçek Uygulamada Nerede Görülür

Otomatik üretilen anahtar, çoğu tabloda varsayılan seçimdir ve ölçümün otomatik-ayni-deger ile otomatik-iki-deger satırları bunun bedelini gösterir. Bir kaydı kaydetme kodu yeniden çalıştığında, veritabanı her seferinde yeni bir kimlik verir ve aynı kayıt iki satır olarak durur. Bir sipariş formunun gönder düğmesine iki kez basılması, bir ağ hatasından sonra isteğin yeniden denenmesi ya da uygulamanın yarım kalmış bir işi yeniden başlatması bu çoğaltmayı üretir. Liste ekranı iki aynı siparişi gösterir; kullanıcı bunu bir hata olarak görür, uygulama ise iki ayrı geçerli kayıt görür.

Anahtarı çağıranın verdiği durumda tablo çoğaltmayı reddeder, ama başka bir sorun çıkar: aynı anahtara iki farklı değer yazan iki işlem varsa kalan değer, hangisinin son çalıştığına bağlıdır. varsa-guncelle-ayni-anahtar satırı bunu gösterir. Bu, tabloların sağlam sayılan bir yerinde bile sıra duyarlılığının sürdüğünü anlatır. Ayrı anahtarlara yazan işlemler ise birbirini etkilemez, ve varsa-guncelle-ayri-anahtar satırı sağlam çıkar.

Bu yüzden veritabanında “sağlam” bir etiket tabloya değil, işlemin hangi anahtarlara dokunduğuna bağlıdır. Aynı tablo, çakışmayan anahtarlarla çalışırken sağlam, aynı anahtara yazılırken sıraya duyarlıdır. Dersin duyarlılık etiketinin sağlam olması, tabloların en çok korunan işlem biçimine, yani anahtarlı güncellemeye dayanır.

Yanlış Kurulum ve Düzeltmesi

En yaygın yanlış kurulum, kaydın var olup olmadığını önce sorgulayıp sonra eklemektir. Önce “bu kayıt var mı” diye bakılır, yoksa eklenir. İki işlem aynı anda bu soruyu sorarsa ikisi de “yok” yanıtını alır ve ikisi de ekler. Kontrol ile ekleme arasındaki aralık, yinelemenin sızdığı yerdir.

Düzeltme, kontrolü ayrı bir adım olarak bırakmak yerine tablonun kendisine yaptırmaktır. Anahtar çağıran tarafından, kayıttan türetilen kararlı bir değer olarak verilir ve tablo aynı anahtarın ikinci kez eklenmesini reddeder ya da var olan satırı günceller. Böylece “var mı, yoksa ekle” tek bir işlem hâline gelir ve araya girilecek bir aralık kalmaz. Ölçümün diliyle: işlem yinelemeye duyarlı olmaktan çıkar.

İkinci yanlış, sorgu sonucunun sırasına güvenmektir. Sıralama belirtilmeyen bir sorgunun satırları hangi sırayla döndüreceği garanti edilmez. Bir ekranın sırası bugün ekleme sırasıyla örtüşse bile, tabloya bir dizin eklendiğinde ya da veri büyüdüğünde değişebilir. Sınırlayıcı ölçümdeki ekleme sırası satırı bunu açıkça kullandı ve KL9 bu varsayımın modelin bir seçeneği olduğunu kaydetti. Düzeltme, sıranın önemli olduğu her sorguda sıralamayı açıkça belirtmektir; anahtar sırası, ölçümde sağlam çıkan okuma biçimidir.

KAYNAK Parçayı Okumak

Dersin başındaki adsız parça, bir tabloyu tanımlayıp ona satır ekleyen ve sonra sorgulayan bir bağlantı çağrısı dizisidir. Gerçek bir bağlantı bir dosyayı açar, sorguyu bir ifade olarak hazırlar, değerleri ifadeye bağlar ve adım adım koşturur. Modelimizdeki tek çağrı, gerçekte bu dört adımın toplamıdır; KL10 bu sadeleştirmeyi ve tablonun tek bir birincil anahtar sütunuyla sınırlı kalmasını kaydeder.

Parçadaki değerler kısalık için doğrudan sorgu metnine yazılmıştır; kullanıcıdan gelen metin bu biçimde sorguya katılırsa sorgunun kendisini değiştirebilir. Gerçek kodda değerler hazırlanan ifadeye bağlanır ve kullanıcı metni sorgu metnine hiç katılmaz. Bu, güvenlik açısından önemli bir alışkanlıktır ve bu dersin ölçümleriyle ilgisi yoktur; yine de gerçek koddaki yerini bilmek için not edilir. Bağlantının her çağrıda kesintisiz çalıştığı varsayımı KL11’dir: iki bağlantının aynı anda yazdığı durum ölçülmedi, ve gerçek veritabanlarında bu durum kilit ve işlem yalıtımı gibi ayrı konulara girer.

Ölçümden Tasarıma

Bu dersin sonucu, tabloların neden güvenli bir varsayılan olduğunu ve bu güvenin nereye kadar uzandığını söyler. Anahtarlı güncelleme sağlamdır, ama yalnız ayrı anahtarlara yazıldığında. Aynı anahtara iki farklı değer yazılıyorsa sıra yine belirleyicidir. Tablonun kattığı şey, çakışmanın görünür olması ve kuralın tabloda yazılı olmasıdır: son yazan mı kazanır, ilk yazan mı, yoksa ikinci yazma reddedilir mi. Bu kuralı kodda saklamak yerine şemada ifade etmek, her yazma yolunun aynı kurala uymasını sağlar.

Tasarım için üç soru yeterlidir. Kaydın doğal bir kimliği var mı? Varsa anahtarı ondan türetmek, yinelemeyi tablonun reddetmesini sağlar. Yoksa, üretilen bir kimlik her zaman çoğaltma riski taşır ve bu risk işlem tarafında, örneğin her istek için bir kez üretilen bir anahtarla yönetilir. İki işlem aynı kayda yazabiliyor mu? Yazabiliyorsa kuralın ne olduğu yazılmalıdır. Okuma sırası önemli mi? Önemliyse sıralama açıkça belirtilmelidir.

Bu üç sorunun hiçbiri veritabanı seçimine bağlı değildir. Sorgu dili ne kadar güçlü olursa olsun, işlemin yinelemeye ve sıraya karşı davranışı kaydın nasıl tanımlandığına bağlıdır. Ölçümün öğrettiği budur: sağlam olan tablo değil, tablonun doğru kullanıldığı işlemdir.

Küçük bir ek uyarı: ölçüm tek tabloya bakar. Gerçek bir veritabanında iki tablo bir ilişkiyle bağlıdır ve bir işlemin ikisine birden yazması gerekir. Bu durumda yarım kalan bir yazma, tutarsız bir veri bırakabilir; bu konu işlem yalıtımına girer ve bu dersin ölçümünün kapsamı dışındadır.

Özet

  • Veri satırlara ve alanlara bölünüp aranıyorsa dosya yerine yerel veritabanı seçilir.
  • Birincil anahtarlı varsa güncelle yoksa ekle iki farklı anahtarda ve sıralı okumada sağlamdır.
  • Aynı anahtara iki farklı değer yazmak sıraya duyarlıdır: ab b2, ba a1.
  • Kendiliğinden artan anahtarla ekleme tabloyu 1=a1’den 1=a1,2=a1’e büyütür; iki farklı değerde çift duyarlıdır.
  • Aynı iki işlem, okuma anahtara göre sıralıysa sağlam, ekleme sırasıyla yapılıyorsa sıraya duyarlıdır.
  • Bu dersin duyarlılığı: sağlam.

Sonraki Adım

Veritabanını doğrudan sorgularla kullanmak yerine nesnelerle çalışan çerçeveler vardır. Sıradaki derste, Nesne Kalıcılık Çerçeveleri, bir nesneyi bağlamda oluşturup kaydetmenin yinelemeye duyarlı olduğu ve benzersizlik kısıtının bunu nasıl değiştirdiği ölçülecek.

İ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