- Büyük SQLite veritabanlarını uzak bir sunucudan yerele taşırken, özgün
.dbdosyası yerine gzip ile sıkıştırılmış SQL dökümünü kopyalamak aktarım miktarını ciddi ölçüde azaltır - Dosyanın büyümesinin başlıca nedeni indekslerdir; sorguları hızlandırmak için tablo verilerini yinelenmiş biçimde saklayarak disk kullanımını artırırlar
- SQLite’ın
.dumpkomutu veritabanını SQL ifadelerinden oluşan metne dönüştürür; indeksler ise gerçek veri olarak değil, tek satırlıkCREATE INDEXolarak temsil edilip yinelenen aktarımı önler - Örnek veritabanında özgün dosya 3.4GB, döküm metni 1.3GB, gzip sıkıştırılmış hali 240MB idi; sıkıştırılmış döküm özgün dosyadan 14 kat daha küçüktü
- Kopyalamadan önce döküm dosyasını sabitlemek, aktarım sırasında özgün dosyanın değişmesiyle
database disk image is malformedhatasının oluştuğu durumları azaltabilir
SQLite dökümünü sıkıştırarak kopyalamak
- Küçük SQLite veritabanlarını doğrudan
rsyncile kopyalamak yeterlidir- Örnek:
rsync --progress username@server:my_remote_database.db my_local_database.db
- Örnek:
- Veritabanı büyüdükçe doğrudan kopyalama yavaş ve kararsız hale gelebilir
- 250MB’lık bir veritabanını bir web sunucusundan ev internetiyle indirmek yaklaşık 1 dakika sürer
- Birkaç GB boyutundaki veritabanlarında bekleme süresi daha da artar
- Boyut artışındaki darboğaz indekslerdedir
- İndeksler sorgu hızını büyük ölçüde artırır, ancak veritabanı dosyasını da büyütür
- Bir veritabanında belirli tek bir indeks, disk boyutunun yarısını kaplıyordu
- İndeksler benzersiz veri barındırmaktan çok, sorguları hızlandırmak için diğer tablo verilerini yineler
- SQLite, veritabanının tamamını bir metin dosyasına dökebilir
- Komut:
sqlite3 my_database.db .dump > my_database.db.txt - Ortaya çıkan dosya
CREATE TABLE,INSERT INTO,CREATE INDEXgibi SQL ifadelerinden oluşur - İndeksler gerçek indeks verisi olarak değil,
CREATE INDEX [idx_photo_locations] ON [photos] ([longitude], [latitude]);gibi oluşturma komutları olarak saklanır
- Komut:
- Döküm dosyası tekrar SQLite veritabanına geri yüklenebilir
- Komut:
cat my_database.db.txt | sqlite3 my_reconstructed_database.db
- Komut:
- SQL dökümlerinde tekrar çok olduğundan gzip sıkıştırması yüksek verim sağlar
- Örnek:
sqlite3 explorer.db .dump | gzip -c > explorer.db.txt.gz - Boyut karşılaştırması:
- Özgün SQLite veritabanı: 3.4GB
- Metin dökümü: 1.3GB
- gzip sıkıştırılmış metin: 240MB
- gzip sıkıştırılmış dosya, özgün SQLite veritabanından 14 kat daha küçüktü
- Örnek:
Gerçek prosedür ve kararlılığa etkisi
- Yeni yöntem, uzak sunucuda sıkıştırılmış bir döküm oluşturup yalnızca bu dosyayı indirdikten sonra veritabanını yerelde yeniden kurar
- Kopyalama prosedürü şöyledir
- Sunucuda gzip sıkıştırılmış metin dosyası oluşturma:
ssh username@server "sqlite3 my_remote_database.db .dump | gzip -c > my_remote_database.db.txt.gz" - Yerele kopyalama:
rsync --progress username@server:my_remote_database.db.txt.gz my_local_database.db.txt.gz - Sunucudaki geçici sıkıştırılmış dosyayı silme:
ssh username@server "rm my_remote_database.db.txt.gz" - Yerelde sıkıştırmayı açma:
gunzip my_local_database.db.txt.gz - Yerel veritabanını yeniden oluşturma:
cat my_local_database.db.txt | sqlite3 my_local_database.db - Yerel metin dosyasını silme:
rm my_local_database.db.txt
- Sunucuda gzip sıkıştırılmış metin dosyası oluşturma:
- Sıkıştırılmış döküm, tutarlı bir kopyalama kaynağı görevi görür
- Büyük bir veritabanı uzun süre kopyalanırken özgün dosya güncellenirse, sonuç dosyasının baş kısmı güncellemeden önceki, son kısmı ise güncellemeden sonraki durumda olabilir
- Böyle bir dosya yerelde açılırken
database disk image is malformedhatası verebilir
- Kopyalamadan önce metin dökümü oluşturmak,
rsynctarafından aktarılan dosyanın aktarım sırasında değişmemesini sağlar- Eksiksiz ve tutarlı bir metin dosyasının kopyalanma olasılığı artar
- Büyük SQLite veritabanlarını sık sık taşıyorsanız bu yöntemle indirme süresini azaltabilir ve kopyalama güvenilirliğini artırabilirsiniz
1 yorum
Hacker News yorumları
Çalışmakta olan ve güncelleme alan bir veritabanı dosyasını olduğu gibi kopyalarsanız elbette bozulması kaçınılmazdır.
SQLite veritabanını güvenli biçimde çoğaltmak için Litestream var: https://github.com/benbjohnson/litestream
Tercih ettiği servisler var ama herhangi bir SFTP sunucusu da olur.
[1] https://github.com/benbjohnson/litestream/issues/140
[2] https://www.rsync.net/resources/notes/2021-q3-rsync.net_technotes.html
Elbette birden fazla dosya varsa hepsinin aynı işlem içinde kopyalanması için bunları bir alt birimde toplamak gerekir; yalnızca
cp --reflink=alwaysyeterli değildir.Süreci
SIGSTOPile durdurmak da benzer bir sonuç verebilir ama buna güvenmek istemem..backupkomutu da çalışan bir veritabanının anlık görüntü sürümünü oluşturup kopyalamayı mümkün kılmak için sunulan resmi araçtır.Doğru hatırlıyorsam mükemmel bir rehber yazmıştı ama onu bulma ihtimalim sıfıra yakın. Bu referansı bilen biri linki paylaşırsa iyi olur.
Bilgisayarlar arasında veritabanı kopyalama denmiş ama sanki sadece daireleri çizip baykuşun geri kalanını atlamışlar.
Başkalarının dediği gibi artımlı
rsyncçok daha hızlı olurdu; ama daha rahatsız edici olan, SQL ifadelerini göndermenin veritabanını göndermekten hızlı olduğunu söyleyip o SQL ifadelerinin çalıştırılması gerektiği gerçeğini tamamen atlamış olmaları. Ardından/optimize/da çalıştırmanız,/vacuum/da çalıştırmanız gerekir.Şu anda CSV dosyalarından veritabanını “artımlı olarak yeniden kurmam” gereken bir durum var; benim durumumda baştan yeniden oluşturmak daha optimum. Yine de çok optimize etmeme rağmen boş bir bellek içi veritabanına toplu ekleme yapmak ve indeksleri oluşturmak tek başına 30 dakika sürüyor.
SQLite3’te hızlı ekleme yapmayı çok iyi özetleyen bir yazı.
250 MB veritabanını indirmek 1 dakika sürüyor denmişti; ben 1 Gbps fiber bağlantı sayesinde şirket sunucusundan 2 GB’lık SQLite test veritabanını 15 saniyede çektim.
Bloom filtresi ve LRU cache gibi hileler kullanmam gerekti ama artık 6 GB’lık veritabanını yaklaşık 20 dakikada oluşturabiliyorum.
SQLite’ta bu amaç için resmi bir araç var: https://www.sqlite.org/rsync.html
Sayfa düzeyinde çalışıyor.
“Protokolde replika, her sayfanın kriptografik hash’ini kaynak tarafa gönderir; kaynak da hash’i eşleşmeyen sayfaların tam içeriğini geri gönderir.”
Bu aracı kullanmak için tek yol kendiniz derlemek.
Yakın zamanda yayımlanan
sqlite_rsyncyardımcı programı, SQLite veritabanlarının iç yapısına göre optimize edilmiş bir rsync algoritması sürümünü kullanıyor. İç veri sayfalarını verimli biçimde karşılaştırdıktan sonra yalnızca değişmiş ya da eksik sayfaları senkronize ediyor.Yazıdaki püf noktaları da iyi ama artık yerleşik yardımcı programı kullanmak daha kolay :)
Nasıl çalıştığını blogda ayrıntılı yazdım: https://nochlin.com/blog/how-the-new-sqlite3_rsync-utility-works
sqlite3_rsyncartık rsync.net platformuna yerleşik olarak dahil.ssh user@rsync.net sqlite3_rsync … blah blah …Daha geçen hafta eklendiği için henüz tüm bölgelere dağıtılmadı, ama ilk kullanıcıların hepsi beklendiği gibi çalıştığını söylüyor.
sqlite_rsyncyalnızca WAL modunda kullanılabilir. WAL modunun ek kısıtı olarak veritabanı dosyasının yerel diskte olması gerekir.Çoğu durumda zaten böyle olur, ama bunun mümkün olmadığı durumlarda bu yardımcı programı kullanamazsınız.
rsync'in aktarım sırasında sunduğu sıkıştırmanın denenmemiş olması şaşırtıcıydı-z, --compressaktarım sırasında dosya verilerini sıkıştırır;--compress-level=NUMise sıkıştırma seviyesini açıkça belirtirMuhtemelen gzip ile sıkıştırıp sonra aktarmak daha hızlı olabilir, ama tek bir bayrakla aktarımı iyileştirebilmek güzel
-zile birliktesqlite3_rsync(https://sqlite.org/rsync.html) kullanmak daha iyi olurBu, SQLite işlemlerini ve WAL'ı bilen bir rsync'e aktarım sırasında sıkıştırma eklemek gibi olur
Böyle işler yaparken dump çıktısını doğrudan gzip'e stream ederim. Genellikle ara dosya olmadan doğrudan hedefe stream etmenin yolunu da bulabilirsiniz
Üstelik böylece hedefte sıkıştırılmış hâlde saklanır. Amaç yoksul işi replikasyon değil de yedekleme ise bu özellikle geçerli
rsynckullanırsanız,rsynckullanmanın asıl sebebi olan rolling checksum tabanlı fark aktarımını tamamen devre dışı bırakmış olursunuz--remove-source-fileskullanılsaydı yeniden SSH ile giriprmçalıştırmaya gerek kalmazdıMetin dosyası olarak saklamak verimsiz. Ben SQLite veritabanını
VACUUM INTOile kaydediyorumsqlite3 -readonly /path/db.sqlite "VACUUM INTO '/path/backup.sqlite';"https://sqlite.org/lang_vacuum.html adresine göre,
INTOyan tümcesiyle kullanılanVACUUMkomutu, çalışan veritabanının yedek kopyasını oluşturan backup API'ye bir alternatiftir. Avantajı, ortaya çıkan yedek veritabanının boyutunun en aza inmesi ve dosya sistemi I/O'sunun azalabilmesidirVACUUM INTOyöntemi ise indeksleri korurMetin dosyası olduğu gibi verimsiz olabilir, ama gzip gibi ilkel araçlarla bile iyi sıkıştırılır. SQLite ikili biçiminin de o kadar iyi sıkışıp sıkışmadığından emin değilim, ama mümkün
DuckDB'de aynı işi yapıp Parquet olarak dışa aktarabilirsiniz; bu durumda veri boyutu metin tabanlı SQL ifadelerine göre tek haneli bir çarpan kadar küçülür. Aktarım da daha hızlı, yükleme de daha hızlı olur
https://duckdb.org/docs/stable/sql/statements/export.html
duckdb -c "attach 'sqlite-database.db' as db; copy db.table_name to 'table_name.parquet' (format parquet, compression zstd)"Benim test veritabanımda, gzip ile sıkıştırılmış metin SQL ifadelerinden yaklaşık %20 daha küçüktü
SQLite'ta, bir tablo kümesindeki değişiklikleri izleyen ve SQLite veritabanının önceki bir sürümüne uygulanabilecek changeset/patchset üreten bir session extension var
https://www.sqlite.org/sessionintro.html
Projelerimden birinde tüm SQL ifadelerini ve parametreleri ayrı bir veritabanına kaydedip onu senkronize ederek yeniden oynatan yoksul işi bir session'ı kendim uygulamıştım. Her gün yalnızca yaklaşık %0,1'i değişen 30 GB civarında bir veritabanı için yeterince iyi çalışıyor
Eski sürümden yeni sürüme düzenli olarak senkronize ediyorsanız gzip'in
--rsyncableseçeneği ile daha da optimize edebilirsinizSıkıştırma oranı yaklaşık %1 düşer, ama bir sürümden sonrakine olan fark sıkıştırılmış çıktının tamamına zincirleme yayılmak yerine yerelleşir
Bir diğer yöntem de dump çıktısını sıkıştırmamak,
rsync'in önceki sıkıştırılmamış dump ile mevcut dump arasındaki farkı hesaplamasına izin vermek ve ağ üzerinden gönderilecek değişiklik kümesini sıkıştırmasını sağlamaktır. Yanirsync -zkullanmakBu, tek bir pipeline olarak işlenebilir gibi görünüyor
ssh username@server "sqlite3 my_remote_database.db .dump | gzip -c" | gunzip -c | sqlite3 my_local_database.db-oCompression=onveya-Cile SSH sıkıştırması açılırsagzip/gunzipgereksiz tekrar olabilirpveklenmesi gerektiği de oldukça açık görünüyor