Neden Veritabanını Doğrudan cp ile Kopyalayamazsınız
SQLite veritabanı tek bir dosyadan ibaret olduğu için, basit bir dosya kopyasıyla yedek almak ilk bakışta cazip görünür. Bazen işe yarar. Çoğu zaman yaramaz.
İki şey ters gidebilir:
- Siz kopyalama yaparken başka bir bağlantı yazma işlemi yürütüyor olabilir. Hedef dosyada yarım kalmış bir transaction kalır — ve dosya açılırken bozuk çıkar.
- Veritabanı WAL modunda olabilir (modern uygulamaların çoğunda varsayılan ayar budur). Yakın zamanda yapılan değişiklikler ayrı bir
database.db-waldosyasında tutulur. Yalnızca ana dosyayı kopyalarsanız, farkına varmadan veri kaybedersiniz.
SQLite bu iş için size düzgün araçlar sunuyor. Bu araçlar; kilitleri, WAL içeriğini ve eşzamanlı yazıcıları sürpriz çıkarmadan yönetir. cp yerine bunlara başvurun.
.backup Komutu ile SQLite Yedekleme
CLI üzerinden bir veritabanını yedeklemenin en hızlı yolu .backup nokta komutudur:
sqlite3 app.db
sqlite> .backup backup.db
sqlite> .quit
Bu komut, app.db veritabanının tam bir kopyasını backup.db olarak çıkarır. Başka süreçler veritabanını okuyor ya da yazıyor olsa bile sorunsuz çalışır; çünkü online backup API tek bir büyük kilit yerine peş peşe küçük kilitler alır, sayfaları parça parça kopyalar ve kopyalama sırasında değişen sayfaları yeniden işler.
Elinizde tamamen kullanılabilir bir SQLite veritabanı olur. Diğer .db dosyaları gibi açabilirsiniz:
sqlite3 backup.db
sqlite> .tables
Tek bir shell komutuyla da halledebilirsiniz; zaten cron job'larının çoğu sonunda şuna benzer bir hâl alıyor:
sqlite3 app.db ".backup '/var/backups/app-$(date +%Y%m%d).db'"
Tek dosya girer, tek dosya çıkar. Dump/restore turu yok, SQL ayrıştırma yok — sadece depolama katmanında sayfalar kopyalanır.
Sıkıştırılmış kopya için VACUUM INTO
VACUUM INTO, akraba ama farklı bir araç. Veritabanının sıfırdan inşa edilmiş bir kopyasını yeni bir dosyaya yazar:
Sonuç aynı mantıksal veritabanıdır; ama sıfırdan yeniden yazılmış halidir — her sayfa sıkıca doldurulmuş, parçalanma yok, silinmiş satırlardan kalma boş sayfa yok. Bu da yedek dosyasını mümkün olduğunca küçük tutar.
Hangisini ne zaman seçmeli:
.backup— rutin, sık alınan yedekler için. Daha hızlıdır, eşzamanlı yazıcılarla iyi geçinir ve bayt bayt birebir kopya üretir.VACUUM INTO— düzenli aralıklarla alacağınız, aynı zamanda derli toplu ve minimum boyutlu olmasını istediğiniz anlık görüntüler için. Her şeyi yeniden yazdığı için daha yavaştır ve işlem süresince kaynak veritabanında yazma kilidi tutar.
İkisi de hemen açabileceğiniz geçerli bir .db dosyası üretir.
Uygulama Kodundan Online Backup API Kullanımı
Bir uygulamanın içinde sqlite3 komut satırına başvurmazsınız. Kullandığınız sürücünün sunduğu online backup API'sini çağırırsınız. Python'un standart kütüphanesindeki sqlite3 modülünde bu, Connection.backup metodudur:
import sqlite3
source = sqlite3.connect("app.db")
dest = sqlite3.connect("backup.db")
with dest:
source.backup(dest)
source.close()
dest.close()
backup metodu, diğer bağlantılar çalışmaya devam ederken source üzerindeki sayfaları dest hedefine kopyalar. İsterseniz pages= ile parça parça kopyalama yapabilir, progress= ile de bir callback alabilirsiniz — büyük veritabanlarında kopyalamayı yavaşlatmak ya da ilerlemeyi göstermek istediğinde işine yarar.
Diğer dillerdeki sürücülerin çoğu aynı C API'sini (sqlite3_backup_init, _step, _finish) benzer isimlerle dışarı açar. Akış hep aynıdır: kaynağı aç, hedefi aç, sayfaları adım adım kopyala, bitir.
SQLite Kullanımdayken Yedek Alma
İşte SQLite'ın sessiz sedasız parladığı yer burası. Hem .backup komutu hem de online backup API, sıcak yedek almak için tasarlanmıştır — kaynak veritabanı tüm süreç boyunca açık ve aktif kalabilir.
Perde arkasında olan şey şu:
- Yedekleme paylaşımlı bir kilit alır ve sayfaları kopyalamaya başlar.
- Bir yazıcı, henüz kopyalanmamış bir sayfayı değiştirirse yedekleme bunu fark eder ve sayfayı yeniden okur.
- Tüm sayfalar tutarlı hale geldiğinde kopyalama tamamlanır.
Uygulamanı durdurmana, bağlantıları koparmana ya da bakım penceresi planlamana gerek yok. Yoğun bir veritabanında yedekleme birkaç tur daha sürebilir ama eninde sonunda tamamlanır. Elinizde kalan hedef dosya, tutarlı bir andan alınmış anlık görüntüyü temsil eder.
Bilmeniz gereken bir nokta: WAL modunda çalışıyorsanız, WAL dosyasının kontrolsüz büyümemesi için ara sıra PRAGMA wal_checkpoint(TRUNCATE); çalıştır. Yedeklemenin kendisi WAL'ı doğru şekilde ele alır — bu öneri, genel WAL hijyeni içindir.
SQLite Yedekten Geri Yükleme
SQLite veritabanını geri yüklemek alışılmadık biçimde sıkıcıdır, ki olayın güzelliği de bu. Yedek dosyasının kendisi zaten bir veritabanı. Kullanmak için sadece açmanız yeterli:
sqlite3 backup.db
sqlite> SELECT COUNT(*) FROM notes;
Canlı bir veritabanının üzerine geri yükleme yapmak — örneğin veri kaybından sonra kurtarma yaparken — için güvenli sıra şudur:
- Veritabanını açık tutan tüm süreçleri durdurun.
- Mevcut
app.db,app.db-walveapp.db-shmdosyalarını silin. Eski veritabanından kalma WAL/SHM dosyaları, geri yüklenen ana dosyayla eşleştiğinde SQLite'ı şaşırtır. - Yedeği yerine kopyalayın:
cp backup.db app.db. - Uygulamanızı yeniden başlatın.
-wal ve -shm dosyaları önemlidir. 2. adımı atlarsanız SQLite, geri yüklenen ana dosyanın üzerine bayatlamış bir WAL'ı uygulamaya çalışabilir; sonuç ya bozulma ya da garip biçimde karışmış veri olur.
CLI içinden ise .backup komutunun karşılığı olan bir .restore komutu da bulunur:
sqlite3 app.db
sqlite> .restore backup.db
sqlite> .quit
Bu işlem, bağlı olan veritabanının içeriğini backup.db dosyasının içeriğiyle baştan üzerine yazar. Arka planda aynı online backup API'sini ters yönde kullanır.
.dump Farklı Bir Araçtır
Eski rehberlerde .dump komutuna sıkça denk gelirsiniz. Aslında bu, klasik anlamda bir yedekleme değildir; size CREATE ve INSERT ifadelerinden oluşan bir SQL metin dosyası üretir:
sqlite3 app.db .dump > app.sql
Geri yüklemek için bu SQL dosyasını tekrar çalıştırırsınız:
sqlite3 new.db < app.sql
Bu yöntem; SQLite sürümleri arasında geçiş yaparken, şemayı git üzerinde karşılaştırırken ya da veriyi başka bir veritabanı motoruna taşırken işinize yarar. Ancak .backup komutuna göre daha yavaştır, dosya boyutu daha büyüktür ve bazı şeyleri kaybedebilir (özel collation'lar, generated column'lar ve bazı pragma'lar ek özen ister). Çalışan bir veritabanının gerçek yedeği için .backup veya VACUUM INTO tercih edin.
Mantıklı Bir SQLite Yedekleme Rutini
Çoğu uygulama için şu kombinasyon iyi sonuç verir:
- Düzenli aralıklarla
.backupçalıştırın — saatlik, günlük, veri kaybına ne kadar tahammülünüz varsa o sıklıkta. Ucuz, hızlı ve sıcak yedek alır. - Haftada bir, ayrı bir konuma
VACUUM INTOile yedek alın. Bu, zaman içinde oluşan tutarsızlıkları yakalar, sıkıştırılmış bir anlık görüntü verir ve farklı bir kod yolunu test etmiş olursunuz. - Bir saklama politikası belirleyin: son N günlük yedeği, son M haftalık yedeği tutun. SQLite veritabanları iyi sıkışır, dolayısıyla yedek alındıktan sonra
gzip backup.dbyapmaya değer. - Ara sıra yedeklerden birini geri yükleyin ve üzerinde birkaç sorgu çalıştırın. Test edilmemiş bir yedek, yedek değil; sadece bir umuttur.
# Cron'da, günlük:
sqlite3 /var/lib/app/app.db ".backup '/var/backups/app-$(date +%F).db'"
gzip "/var/backups/app-$(date +%F).db"
# Haftalık:
sqlite3 /var/lib/app/app.db "VACUUM INTO '/var/backups/app-weekly-$(date +%F).db'"
Her iki komutu da uygulama istekleri karşılarken çalıştırmak güvenlidir.
Sırada: PRAGMA Ayarları
Yedekleme, operasyonel konulardan yalnızca biri; bir diğeri de çalışma zamanı davranışını ince ayarlamak. SQLite, ayar düğmelerini PRAGMA ifadeleri üzerinden sunar — journal mode, synchronous seviyesi, cache size, foreign key zorlaması. Bir sonraki sayfada bilmeye değer olanları tek tek ele alıyoruz.
Sıkça Sorulan Sorular
SQLite veritabanını nasıl yedeklerim?
CLI üzerinden, kaynak veritabanına bağlıyken .backup yol/yedek.db komutunu çalıştırmanız yeterli. Uygulama kodu içinden ise online backup API'sini kullanın (C tarafında sqlite3_backup_init, diğer dillerde sürücünün karşılığı). Her iki yöntem de başka bağlantılar yazma yapıyor olsa bile tutarlı bir kopya üretir.
Yedek almak için .db dosyasını doğrudan kopyalayabilir miyim?
Sadece hiçbir sürecin veritabanını yazmak için açık tutmadığından eminseniz. Aksi hâlde dosyayı bir transaction'ın ortasında kopyalayıp bozuk bir yedekle baş başa kalabilir ya da WAL dosyasındaki veriyi atlayabilirsiniz. Onun yerine .backup veya VACUUM INTO kullanın — bunlar kilit ve WAL içeriklerini doğru yönetir.
.backup ile VACUUM INTO arasındaki fark nedir?
.backup online backup API'sini kullanır ve kullanılmayan sayfalar dahil bayt bayt birebir kopya üretir. VACUUM INTO 'dosya.db' ise yepyeni, sıkıştırılmış bir kopya yazar — daha küçük ve parçalanmamış olur, ama her sayfayı yeniden yazar. Rutin yedekler için .backup, aynı zamanda yer kazanmak istediğinizde VACUUM INTO tercih edin.
SQLite veritabanını yedek dosyasından nasıl geri yüklerim?
Yedek bir .db dosyasıysa açmanız yeterli — SQLite veritabanları tek dosyadır. Mevcut bir veritabanının üzerine geri yüklemek için uygulamanızı durdurun, dosyayı değiştirin (geride kalan -wal/-shm dosyalarını da silin) ve yeniden açın. CLI'dan boş bir veritabanına bağlıyken .restore yol/yedek.db komutunu da çalıştırabilirsiniz.