Ders 18 / 25
Yedek Doğrulama
Yedeğin geçerliliğinin geri yükleme anından önce bilinmesi: dosya karmasıyla bütünlük denetimi, sessiz bozulmanın içerik karmasıyla yakalanması, yapısal denetimin kapsamı ve düzenli kurtarma tatbikatının kurulması.
İçindekiler
Önceki iki ders yedek almayı ve yedekten dönmeyi kurdu. İkisi de aynı varsayıma dayanıyordu: yedek dosyası okunabilir ve içeriği doğru. Bu varsayım sınanmadığı sürece bir varsayımdır ve sınanmadığı sürece de doğru kabul edilir — arıza anına kadar.
Yedeklemenin en pahalı kusuru, alınmayan yedek değildir; alındığı sanılan yedektir. Disk dolduğu için yarım yazılmış bir dosya, kopyalama sırasında bozulmuş bir bayt ya da haftalardır sessizce başarısız olan bir yedekleme görevi, hepsi aynı sonucu verir: kurtarma anında elde kullanılamaz bir dosya.
Bu ders yedeğin geçerliliğini geri yükleme anından önce bilmenin yollarını kuruyor. Üç ayrı denetim vardır ve üçü ayrı şeyleri yakalar.
Üç Denetim, Üç Kapsam
Dosya karması. Yedek alındığı anda dosyanın karması hesaplanır ve dosyanın yanında saklanır. Sonradan yapılan karşılaştırma, dosyanın tek baytının bile değişmediğini gösterir. Aktarım ve saklama sırasındaki bozulmaları yakalar; yedeğin içeriğinin doğru olduğunu göstermez.
Yapısal bütünlük denetimi. Motorun kendi denetimi, veritabanı dosyasının sayfa yapısının, ağaç bağlantılarının ve dizinlerin tutarlı olup olmadığını sınar. Yapıyı denetler, değerleri denetlemez.
İçerik karması. Veritabanının satır değerlerinden hesaplanan karma, yedek alındığı anda kaydedilir ve geri yüklendikten sonra yeniden hesaplanarak karşılaştırılır. Değerdeki bozulmayı yakalayan tek denetim budur.
Mantıksal Yedeğin İki Bozulma Biçimi
Aşağıdaki blok bir yedek alıyor, iki ayrı biçimde bozuyor ve üç dosyayı aynı tatbikattan geçiriyor. Birinci bozulma tek bir baytın değişmesi — bir ödünç kaydının kitap numarası başka bir sayıya dönüşüyor. İkincisi dosyanın son onda birinin kesilmesi, yani yarım yazılmış bir yedek.
Blok yalnız kendi ürettiği geçici dosyalar üzerinde çalışır ve hiçbir yedeği yerinde değiştirmez; bozuk sürümler ayrı dosyalara yazılır. Gerçek bir kurulumda tatbikat da böyledir: yedek dosyasına dokunulmaz, kopyası üzerinde çalışılır.
rm -f kutuphane.db yedek.sql bozuk.sql kesik.sql tatbikat.db sqlite3 kutuphane.db <<'SQL' CREATE TABLE uye(id INTEGER PRIMARY KEY, ad TEXT NOT NULL, sube_id INT NOT NULL); CREATE TABLE odunc(id INTEGER PRIMARY KEY, kitap_id INT NOT NULL, uye_id INT NOT NULL, sube_id INT NOT NULL, alis TEXT NOT NULL, iade TEXT); WITH RECURSIVE s(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM s WHERE n<5000) INSERT INTO uye(id,ad,sube_id) SELECT n,'Uye-'||n,(n%3)+1 FROM s; WITH RECURSIVE s(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM s WHERE n<40000) INSERT INTO odunc(id,kitap_id,uye_id,sube_id,alis,iade) SELECT n,(n%500)+1,(n%5000)+1,(n%3)+1,'2025-06-01',NULL FROM s; SQL sqlite3 kutuphane.db .dump > yedek.sql shasum -a 256 yedek.sql | awk '{print "yedek karmasi (alindigi an): " substr($1,1,16)}' # Iki bozulma bicimi uretilir: tek bayt degisimi ve dosyanin kesilmesi. node -e ' const fs = require("node:fs"); const b = fs.readFileSync("yedek.sql"); const im = b.indexOf("VALUES(20000,"); const yer = im + "VALUES(20000,".length; // kitap_id alaninin ilk basamagi const c = Buffer.from(b); c[yer] = b[yer] === 0x39 ? 0x38 : b[yer] + 1; // basamagi degistir fs.writeFileSync("bozuk.sql", c); fs.writeFileSync("kesik.sql", b.subarray(0, Math.floor(b.length * 0.9))); console.log("kaynak: " + b.subarray(im, im + 37).toString()); console.log("bozuk : " + c.subarray(im, im + 37).toString()); ' kaynak_karma=$(sqlite3 kutuphane.db '.sha3sum --schema --sha3-256') echo echo "yedek | dosya karmasi | geri yukleme | odunc satir | icerik karmasi" echo "------------|---------------|--------------|-------------|---------------" for dosya in yedek.sql bozuk.sql kesik.sql; do rm -f tatbikat.db dosya_karma=$(shasum -a 256 "$dosya" | awk '{print substr($1,1,8)}') if sqlite3 tatbikat.db < "$dosya" 2>/dev/null; then yukleme="tamam"; else yukleme="HATA"; fi satir=$(sqlite3 tatbikat.db 'SELECT COUNT(*) FROM odunc;' 2>/dev/null || echo "yok") icerik=$(sqlite3 tatbikat.db '.sha3sum --schema --sha3-256' 2>/dev/null | tail -1) if [ "$icerik" = "$kaynak_karma" ]; then esit="ESIT"; else esit="FARKLI"; fi printf "%-11s | %-13s | %-12s | %11s | %s\n" "$dosya" "$dosya_karma" "$yukleme" "$satir" "$esit" done rm -f tatbikat.db
yedek karmasi (alindigi an): d33cffac34951285 kaynak: VALUES(20000,1,1,3,'2025-06-01',NULL) bozuk : VALUES(20000,2,1,3,'2025-06-01',NULL) yedek | dosya karmasi | geri yukleme | odunc satir | icerik karmasi ------------|---------------|--------------|-------------|--------------- yedek.sql | d33cffac | tamam | 40000 | ESIT bozuk.sql | fb6cf157 | tamam | 40000 | FARKLI kesik.sql | 799bd4e0 | HATA | yok | FARKLI
Orta satır bu dersin gerekçesidir. Bozuk yedek hatasız geri yüklendi. Satır sayısı kaynakla aynı çıktı. Tablolar yerinde, dizinler kurulu, sorgular çalışıyor. Yalnız içerik karması farklı — ve fark, kırk bin satırın birindeki bir sayıdan ibaret.
Bu bozulma biçimi kendini göstermez. Kırk bin ödünç kaydından birinin kitap numarası yanlış olduğunda hiçbir sorgu hata vermez; yalnız o kitabı arayan kişi bulamaz. Yedeği “başarıyla geri yükledik” diye kabul eden bir tatbikat bu kusuru geçirir.
Alt satır ise farklı bir şey öğretiyor. Kesik yedek geri yüklenmedi ve tablo bile oluşmadı. Nedeni, mantıksal yedeğin tek bir işlem olarak yazılmasıdır: dosyanın başında bir işlem başlatılır, sonunda kesinleştirilir. Dosya yarım kaldığında kesinleştirme gelmez ve her şey geri alınır. Bu, kısmi geri yüklemeden iyidir — yarım bir veritabanı, boş bir veritabanından tehlikelidir, çünkü kullanılabilir görünür.
Dosya karması sütunu üç dosya için de farklıdır. Yedek alındığı anda kaydedilen karma ile karşılaştırıldığında iki bozulma da geri yüklemeye hiç girişmeden yakalanırdı. Bu, en ucuz denetimdir ve her yedeğin yanında saklanması gereken tek satırlık bilgidir.
Fiziksel Yedekte Yapısal Denetim
Fiziksel yedekte durum başkadır: dosya bir metin değil, sayfalardan oluşan bir yapıdır. Motor bu yapıyı kendisi sınayabilir. Aşağıdaki blok aynı veritabanının iki kopyasını iki ayrı konumdan bozuyor — biri bir sayfa başlığını, diğeri bir veri değerini.
rm -f fiziksel.db sqlite3 fiziksel.db <<'SQL' CREATE TABLE odunc(id INTEGER PRIMARY KEY, kitap_id INT NOT NULL, uye_id INT NOT NULL, sube_id INT NOT NULL, alis TEXT NOT NULL); WITH RECURSIVE s(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM s WHERE n<40000) INSERT INTO odunc(id,kitap_id,uye_id,sube_id,alis) SELECT n,(n%500)+1,(n%5000)+1,(n%3)+1,'2025-06-01' FROM s; CREATE INDEX odunc_uye ON odunc(uye_id); SQL kaynak_karma=$(sqlite3 fiziksel.db '.sha3sum --schema --sha3-256') # Iki ayri konumda tek bayt bozulur: sayfa basligi ve bir veri degeri. for yer in 40960 200000; do cp fiziksel.db bozuk-$yer.db node -e " const fs = require('node:fs'); const fd = fs.openSync('bozuk-$yer.db', 'r+'); const b = Buffer.alloc(1); fs.readSync(fd, b, 0, 1, $yer); fs.writeSync(fd, Buffer.from([b[0] ^ 0xff]), 0, 1, $yer); fs.closeSync(fd); " done echo "dosya | integrity_check | icerik karmasi" echo "-------------------|-----------------|---------------" for d in fiziksel.db bozuk-40960.db bozuk-200000.db; do ic=$(sqlite3 "$d" 'PRAGMA integrity_check;' 2>&1 | head -1) karma=$(sqlite3 "$d" '.sha3sum --schema --sha3-256' 2>/dev/null | tail -1) if [ "$karma" = "$kaynak_karma" ]; then esit="ESIT"; else esit="FARKLI"; fi printf "%-18s | %-15s | %s\n" "$d" "${ic:0:15}" "$esit" done rm -f bozuk-40960.db bozuk-200000.db
dosya | integrity_check | icerik karmasi -------------------|-----------------|--------------- fiziksel.db | ok | ESIT bozuk-40960.db | Error: stepping | FARKLI bozuk-200000.db | ok | FARKLI
Bozulan bayt bir sayfa başlığındaysa yapısal denetim bunu yakalıyor: motor sayfayı
okuyamıyor ve dosyanın bozuk olduğunu bildiriyor. Bozulan bayt bir veri değerinin içindeyse
yapısal denetim ok diyor — yapı tutarlıdır, yalnız değer yanlıştır.
Buradaki bayt konumları bu motorun sayfa boyutuna ve tabloların dosyadaki yerleşimine bağlıdır; sizde farklı konumlar farklı sonuç verebilir. Sonucun kendisi motora özgü değildir: sayfa yapısını denetleyen hiçbir araç, sayfanın içindeki bir sayının doğru sayı olduğunu bilemez.
Bu, yapısal denetimin sınırıdır ve içerik karmasının neden ayrıca gerektiğini gösterir.
Kurtarma Tatbikatı
Yukarıdaki iki blok bir tatbikatın parçalarıdır. Bütünü şu adımlardan oluşur ve düzenli aralıklarla, kimsenin acelesi yokken çalıştırılır.
Yedek dosyasının karması, alındığı andaki karmayla karşılaştırılır. Uymuyorsa saklama ya da aktarım katmanında bir sorun var demektir; geri yüklemeye girişilmez.
Yedek, üretim dışı bir ortama geri yüklenir. Hedef her zaman yeni ve boş bir veritabanıdır. Çalışan bir veritabanının üzerine tatbikat yapılmaz.
Yapısal bütünlük denetimi çalıştırılır. Fiziksel yedeklerde sayfa ve dizin tutarlılığını doğrular.
İçerik karması, yedeğin alındığı anda kaydedilmiş karmayla karşılaştırılır. Bu adım, satır sayısı karşılaştırmasının yerine geçer — sayılar tutup değerler bozuk olabilir.
Süre ölçülür. Geri yüklemenin ne kadar sürdüğü, kurtarma süresi hedefinin karşılanabilir olup olmadığını söyleyen tek sayıdır. Bu sayı veri büyüdükçe artar; ölçülmediği sürece plan bir varsayımdır.
Uygulama, kurtarılan kopyaya bağlanır. Yedeğin geri yüklenmiş olması, uygulamanın çalışacağı anlamına gelmez: eksik bir dizin, kaydedilmemiş bir görünüm ya da yedeğe girmemiş bir yapılandırma tablosu ancak burada ortaya çıkar.
Tatbikatın çıktısı bir rapor değil, bir karardır: yedek geçerli ya da değil. Aradaki “muhtemelen iyidir” durumu, yedeğin doğrulanmamış olmasıyla aynı şeydir.
Tatbikatın Sıklığı ve Kapsamı
Sıklık, kurtarma noktası hedefiyle birlikte belirlenir. Yedeği doğrulanmamış geçen her gün, o günün yedeğinin güvenilmez olması riskidir. Yedekleme günlükse tatbikat da sık olmalıdır; tam tatbikat pahalıysa, hafif bir bütünlük denetimi her yedek için, tam geri yükleme tatbikatı daha seyrek çalıştırılır.
Kapsam, yedeklenen her nesneyi içerir. Kütüphane örneğinde ödünç tablosu yedekleniyorsa ama şube tanımları ayrı bir yapılandırma dosyasında duruyorsa, tatbikat kütüphaneyi çalışır hâle getirmez. Yedeğin kapsamı, uygulamanın çalışması için gereken her şeydir; tatbikat bunu ortaya çıkarmanın yoludur.
Son olarak tatbikatın kendisi kayıt bırakır: hangi yedek, hangi tarihte, ne kadar sürede geri yüklendi ve karmalar uydu mu. Bu kayıt, bir sonraki arızada “en son ne zaman denedik” sorusuna yanıt verir.
Özet
- Yedeğin geçerliliği alındığı anda değil, doğrulandığı anda bilinir; doğrulanmamış yedek alınmamış yedekle aynı güvenceyi verir.
- Dosya karması bozulmayı geri yüklemeye girişmeden yakalar ve her yedeğin yanında saklanır.
- Tek bayt değişmiş bir mantıksal yedek hatasız geri yüklenebilir ve satır sayısı tutabilir; bu bozulmayı yalnız içerik karması yakalar.
- Yapısal bütünlük denetimi sayfa ve dizin tutarlılığını sınar, değerlerin doğruluğunu sınamaz.
- Kesik bir mantıksal yedek, tek işlem olarak yazıldığı için kısmi değil boş bir veritabanı üretir.
- Kurtarma tatbikatı karma karşılaştırması, yapısal denetim, süre ölçümü ve uygulamanın bağlanmasını içerir; çıktısı geçerli/geçersiz kararıdır.
Sonraki Adım
Yedek ve kurtarma, veriyi kaybetmemeyi güvence altına alır; sistemin kesintisiz çalışmasını değil. Kırk dakika süren bir geri yükleme, o kırk dakika boyunca kütüphanenin ödünç veremeyeceği anlamına gelir. Kesintiyi kısaltmanın yolu, verinin bir kopyasının başka bir makinede zaten hazır durmasıdır. Sonraki ders fiziksel çoğaltmayı ele alıyor: birincil sunucunun günlük kayıtlarının bir yedek sunucuya sürekli akıtılması, bu akışın gecikmesi ve gecikmenin okuma tutarlılığına etkisi.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.