- Küçük ölçekli web sitelerinin üretim ortamında da SQLite kullanılabilir, ancak Django ORM’in üstlendiği işler arttıkça veritabanının operasyonel karmaşıklığını hissettim
- 4.000 satırlık bir tabloda FTS5 araması 5 saniye sürüyordu;
ANALYZEçalıştırıldıktan sonra yaklaşık 0,05 saniyeye düştü ve sorgu planlayıcı istatistiklerinin performansı ciddi biçimde etkileyebileceği görüldü - Büyük ölçekli
DELETEişlemleri 5 saniyeyi aşarsa diğer yazma işlemleri de zaman aşımına uğrayabiliyor, worker’lar ve VM bile kapanabiliyor; bu yüzden temizlik işleri küçük batch’lere bölünerek işleniyor - Yedekleme için
VACUUM INTOve restic kullanıldı ancak OOM nedeniyle sonlanma yaşandı; daha verimli artımlı yedekleme için Litestream de deneniyor - Tablolar birden fazla SQLite dosyasına ayrılabilir; Mess with DNS’in 2022’den bu yana 4 yıl SQLite ile çalıştığı ve proje için uygun bir tercih olduğu değerlendiriliyor
Bir Django sitesinde SQLite işletmek
- Küçük ölçekli üretim sitelerinde de SQLite kullanılabileceğini anlatan çeşitli yazılara bakılarak Django sitesinin veritabanı olarak seçildi
- SQLite da operasyon bilgisi gerektiren karmaşık bir veritabanıdır; bu projede Django ORM üzerinden önceye göre daha fazla iş ona yaptırılıyor
- Çeşitli önerilere uygun olarak en baştan WAL modu etkinleştirilerek işletildi
- Bu site, SQLite kullanılan dördüncü web sitesi
ANALYZE arama performansını nasıl değiştirdi
- 4.000 satırlık bir tabloda SQLite FTS5 kullanan tam metin arama sorgusu 5 saniye sürüyordu
ANALYZEçalıştırılınca aynı sorgu yaklaşık 0,05 saniyeye indi; daha fazla araştırmaya gerek kalmayacak kadar hızlandı- Tam olarak hangi sorgu planı sorunu olduğu doğrulanamadı; istemeden oluşan ikinci dereceden zaman karmaşıklığına benzer bir durum olduğu tahmin ediliyor
ANALYZE, tablo satır sayıları gibi istatistik bilgileri oluşturarak sorgu planlayıcının daha iyi seçimler yapmasına yardımcı olur- Sorgu planlarını okuma yöntemi henüz öğrenilmiş değil
Veri temizliği ve tek yazma kısıtı
- django-tasks-db içindeki tamamlanmış işler gibi gereksiz satırlar toplu halde silinirken zincirleme sorunlar ortaya çıktı
- Çok sayıda satırı işleyen temizlik komutu 5 saniyeden uzun çalıştı
- Bu sırada diğer worker’ların veritabanına yazmaları, ayarlanmış 5 saniyelik sınırı aşıp zaman aşımına uğradı
- Yazması başarısız olan worker çöktü ve VM de kapandı
DELETEişleminin neden yavaş olduğunun tam nedeni doğrulanamadı; bunun, transaction içinde çok fazla Python kodu çalışmasından kaynaklanıyor olabileceği de değerlendiriliyor- Şu anda her veritabanı sorgusunun 5 saniyeyi aşmaması için temizlik işleri küçük batch’lere bölünüyor
- Bu deneyim, aynı anda birden fazla yazma işlemini işleyebilen Postgres gibi bir veritabanına neden ihtiyaç duyulabileceğini hissettirdi
- Gelecekte bu tür işler sırasında siteyi kapatıp planlı bakım yapma seçeneği de değerlendiriliyor, ancak bunun için henüz bir iş akışı hazırlanmış değil
ORM sorguları ve mevcut veri ölçeği
- Şimdiye kadar Django ORM ile istenen sorgular oluşturulurken performans ayrıca kontrol edilmedi, ancak
ANALYZEsorunu dışında çoğu şey normal çalıştı - Veritabanı yaklaşık 10.000 satır ile küçük; gelecekte de küçük ölçekte kalması bekleniyor
SQLite yedekleme yöntemleri
- SQLite yedekleri için restic ve Litestream olmak üzere iki yöntem denendi
- Yedekleme işleri genellikle dead man's switch ile izleniyor, ancak gerçek geri yükleme testinin henüz yapılmadığı anlaşılıyor
-
restic ile yedekleme
VACUUM INTOile veritabanının bir kopyası oluşturulup gzip ile sıkıştırıldıktan sonra S3’e yükleniyor- restic ile yedek oluşturma, snapshot kontrolü, eski yedekleri silme ve temizlik yapılıyor
- Yedekleme bazen OOM nedeniyle sonlanınca kilit kaldı ve
restic unlockda çalıştırıldı
sqlite3 /data/calendar.db "VACUUM INTO '/tmp/calendar.sqlite'" gzip /tmp/calendar.sqlite restic -r s3://s3.amazonaws.com/some_bucket/ unlock restic -r s3://s3.amazonaws.com/some_bucket/ backup /tmp/calendar.sqlite.gz restic -r s3://s3.amazonaws.com/some_bucket/ snapshots restic -r s3://s3.amazonaws.com/some_bucket/ forget -l 1 -H 6 -d 2 -w 2 -m 2 -y 2 restic -r s3://s3.amazonaws.com/some_bucket/ prune -
Litestream ile artımlı yedekleme
- restic yedeklerinin OOM nedeniyle sonlanmasını önlemek ve artımlı yedeklemenin verimliliğini denemek için Litestream kullanılmaya başlandı
- Yapılandırma dosyası yazıldıktan sonra çoğaltma şu komutla çalıştırılıyor
litestream replicate -config litestream.yml- Veritabanı geçmişini belirli bir süre tutmak için
retention: 400hbelirtildi, ancak bunun amaçlandığı gibi çalışıp çalışmadığı henüz doğrulanmadı - AWS’e yedekleme yapılıyor, ancak konsoldan kimlik bilgisi oluşturmak zahmetli olduğu için gelecekte başka bir S3 uyumlu servise geçme seçeneği de değerlendiriliyor
Birden fazla SQLite veritabanı dosyası kullanmak
- Mevcut proje yalnızca tek veritabanı kullanıyor, ancak Mess with DNS içinde birlikte durması gerekmeyen tablolar 3 veritabanı dosyasına ayrıldı
- Bu ayrımın operasyon açısından faydalı olduğu düşünülüyor
- Mess with DNS, 2022’den bu yana 4 yıl boyunca SQLite ile çalıştı ve o proje için Postgres’ten SQLite’a geçmek iyi bir tercihti
İşletme sırasında sonradan fark edilen temel özellikler
- Bir web projesinde SQLite’ın ilk kullanıldığı zaman 2022 olsa da
ANALYZE’ın varlığı bu kez ilk defa öğrenildi - Kullanılan teknolojinin temel özellikleri bile gerçek işletim sürecinde yıllar içinde öğrenilmeye devam ediyor
1 yorum
Hacker News yorumları
SQLite’in
.expertmodunu kullanırsanız sorgu planı okumayı öğrenmeniz gereken günü biraz erteleyebilirsiniz: https://www.sqlite.org/cli.html#index_recommendations_sqlite...SELECT * FROM x1 WHERE a=? AND b>?;sorgusunu analiz ettiğinizdeCREATE INDEX x1_idx_000123a7 ON x1(a, b);gibi önerilen indeksi gösterir; bunu oluşturup yeniden analiz ettiğinizde yeni bir indeks gerekmediğini belirtirPostgres gibi “gerçek” veritabanlarında da temizlik işlerini genellikle küçük batch’lere bölmek önerilir. Sadece küçük ölçekte bunun verimsiz bir işlem olduğu gerçeği daha az görünür olur; bu yüzden asıl yazıdaki yaklaşım sanılandan daha doğruydu
Sık commit etmek yardımcı olur, ancak büyük veritabanlarını düzenli olarak temizlerken en etkili yöntem partitioning oldu. En eski partition’ı silmek neredeyse anında biter
Yine de “worker’lar veritabanına yazamadığı için çakışıyor ve VM kapanıyor” ifadesinde VM’in neden kapandığı belirsiz. Buradaki VM, işletim sisteminin çalıştığı sanal makineyi ifade ediyor gibi görünüyor
UPDATEveyaDELETEişlemlerini batch’ler halinde çalıştırmak gerekiyordu. Çünkü tek bir sorguyla yapılırsa güncellenen milyonlarca satırın tüm replikalara bir seferde gönderilmesi gerekirEXPLAIN QUERY PLAN’a benziyor: https://sqlite.org/eqp.htmlNormal
EXPLAINgenellikle gerekenden fazla ayrıntılı bytecode çıktısı üretir, ancakEXPLAIN QUERY PLANözetlenmiş planı gösterirVeritabanlarıyla çalışan biri olarak okuması sinir bozucuydu; nedeni kendim bulup çözmek istedim. Yalnızca 10 bin satırlık bir tablo söz konusuysa tam tablo taraması bile çok hızlı olmalı; aynı fiziksel sunucuda süreç içinde çalışan SQLite için bu daha da geçerli
Aklıma gelen çözüm doğal olarak “indeks oluştur”du. Yavaş silme işlemi, ORM kullanıcılarının alttaki veritabanı etkileşimlerini anlamadan önce sık yaşadığı klasik N+1 problemi olabilir gibi görünüyor; Julia’nın bir devam yazısı yazmasını umuyorum
AWS konsolunda kimlik bilgisi oluşturmak çok zahmetli olduğu için birkaç yıl önce yalnızca bu sorunu çözen bir araç yaptım
uvx s3-credentials create my-existing-s3-bucketBu komut, kapsamı yalnızca ilgili bucket ile sınırlı okuma-yazma kimlik bilgileri üretir.
--read-only,--write-onlyile yetkileri daha da kısıtlayabilir veya--prefix foo/barile yalnızca belirli bir prefix ile başlayan key’leri okuyup yazmasını sağlayabilirsinizRestic ve Cloudflare R2 ile birlikte de denedim; çok iyi çalıştı
LLM çağına girince Julia’nın yazılarını daha da takdir eder oldum. Samimi bir keşif süreci, kendinden emin biçimde her şeyi biliyormuş gibi yapan otomatik üretilmiş yazılara karşı panzehir gibi
Yedeklemeyi şöyle çalıştırıyorum:
OUT="${i}.sql.zst"PART="${OUT}.part"sqlite3 -readonly "${i}" .dump | zstd --fast --rsyncable -v -o "${PART}" -mv "${PART}" "${OUT}"Yazma worker’ları WAL kullanıyorsa bunu engellemez ve hem iyi sıkıştırılan hem de senkronize etmesi kolay bir dump üretir. Home Assistant veritabanı 1,8 GB, ancak sıkıştırılmış dump 286 MB; her gün üretilen içeriğin yaklaşık %90’ının aynı olduğunu tahmin ediyorum
Veritabanının bu kadar büyümesinin uzun süre saklanan zaman serisi kayıtlarından kaynaklanıp kaynaklanmadığını da merak ediyorum
VACUUM INTO,.backup,sqlite3_rsyncve Litestream de yazma worker’larını engellemez..backup, yedekleme API’sini kullanır.dumpyüzünden bir kez kilitlenme yaşadıktan sonra.backup’a geçtim. Yine de.partdosyasına yazıp sonramvyapmak temiz bir yöntem“Daha fazla araştırmadım”, “en iyi tahminim bu”, “belki başka şeyler de?”, “transaction içinde çok fazla Python kodu çalışıyor olabilir” gibi ifadelerden ibaret olduğu için yazıda elle tutulur bir içerik olmadığını düşünüyorum. Doğru düzgün öğrenmeden ya da araştırmadan, bazen hatalı tahminler bile yapıyor.
Debian kullanıcısı olarak Linux ile ilgili arama sonuçlarında Ubuntu forumları çıktığında açmamamın nedeni de yanlış tahminlerin çok fazla olması. Buna karşılık Arch, Debian’dan epey farklı olsa da bilgili kişilerin hazırladığı Arch Wiki’yi genelde kontrol ederim
Bu yazı, dünyanın en büyük SQLite uzmanının açıklaması gibi davranmıyor; daha başlığından “SQLite işletimi hakkında birkaç şey öğrenmek” diyerek beklentiyi net biçimde belirliyor.
Her şeyi bilmeye ya da biliyor gibi yapmaya gerek olmadan, basit yöntemlerle sorunu anlayıp bilgi biriktirebileceğimiz mesajı Julia’nın yazılarının genelinde var. Şimdiye kadar öğrendiklerini olabildiğince açık paylaşmak iyi bir tutum.
Ben de daha dün bir özellik geliştirirken 2 programlama dili, 2 build sistemi, bir bulut sağlayıcısı, gizli bilgi yönetimi aracı, iki dile yayılan karmaşık bir istemci-sunucu iletişim framework’ü, sürüm kontrol sistemi, editör ve CI aracı kullandım. Buna işletim sistemi ve runtime sürümleri, veritabanı, reverse proxy, cache ve domain logic de eklenebilir.
Karşılaştığınız her konuyu derinlemesine kurcalarsanız hiçbir işi bitiremezsiniz; bu yüzden yazar gibi odaklanılacak problemi seçmek gerekir.
DELETEsorunu toplu silme, gruplar arasında gecikme koyma veSELECTilerowiddeğerlerini önceden çekme yöntemiyle kolayca hafifletilebilir.SELECTbloklamaya neden olmaz.Veriler aynı tabloya sıralı biçimde eklendiyse dosyada da benzer sırayla saklanmış olma olasılığı yüksektir; bu nedenle o sırayla ya da ters sırayla silmek daha hızlı olabilir. Sonuç, depolama ortamına ve diğer koşullara göre değişir.
SELECTsorgusunu replica’ya gönderebiliyorduk; satır filtrelemesinin yol açtığı indeks belleği baskısı, veritabanının CPU’su ve buffer cache’i üzerinde ciddi yük oluşturuyordu.Partition pruning gibi yöntemlerle gereksiz verileri ana işlem yolunun dışına çıkarmak zorsa, bu çok güçlü bir stratejidir.
Şu an aşina olduğunuz seviyeden ya da işin gerektirdiğinden veritabanını biraz daha derin kurcalamak hâlâ beceriyi artırmanın iyi bir yolu. Pek çok web geliştiricisi veritabanı araçları karşısında tıkanıp kalıyor; ben de K8s gibi bazı operasyon teknolojilerinde benzer şekilde tıkanıyorum.
SQL’in diskte veri okuma ve yazma işlemlerine nasıl dönüştüğünü bilmek, hangi yaklaşımın makul olacağını sezgisel olarak değerlendirmede çok yardımcı olur. Veritabanının kilitleme modelini de birlikte anlamak gerekir.
Bu bilgiye sahip olunca Postgres’te basit bir
COUNTbile hızlı bitmediğinde paniğe kapılma ihtimaliniz azalır.sqlite_stat1vesqlite_stat4, indeks değer dağılımına ilişkin çeşitli istatistikleri saklar; query planner da bunlara dayanarak indeksin seçiciliğini ve faydasını tahmin eder.sqlite_stat1, indeksin kayıt sayısı ve değer başına ortalama kayıt sayısını sağlar; etkinleştirildiğindesqlite_stat4histogram verilerini de saklar.Litestream çok ilginç; S3’ü backend olarak bağlayıp çalıştırmayı başardım. SQLite kullanan sayısız uygulamayı dosya sistemi durumuna neredeyse hiç bağımlı olmayacak hâle getirebilir.
S3’ün durumunu yönetmek çok daha kolaydır; yedekleme ve senkronizasyonu da sağlayıcı halleder.