1 puan yazan GN⁺ 4 시간 전 | 1 yorum | WhatsApp'ta paylaş
  • SQLite'ta birincil anahtar uygulaması, normal rowid tabloları ile WITHOUT ROWID tablolarında fiziksel depolama sırasını farklı biçimde oluşturur; rastgele UUID4 kümelenmiş indeks olarak kullanıldığında B-tree yeniden dengelemesi ve ek sayfalama maliyeti ortaya çıkar
  • Tamsayı rowid taban çizgisi, 1 milyon satırlık ekleme birimlerinde kabaca saniyede 1 milyon ekleme düzeyindeyken, UUID4 WITHOUT ROWID ekleme süresinde 14-16 kat daha yavaş sonuç verdi
  • UUID4'ün sırasız yapısı, satırların B-tree'ye rastgele eklenmesine yol açar ve profil sonuçlarında ağaç dengeleme ile okuma-yazmaya daha fazla zaman harcandığı görülür
  • UUID7 WITHOUT ROWID, zaman sıralı UUID olarak UUID4'ün sıralama sorununu azaltarak daha makul ekleme süreleri gösterdi; ancak 16 baytlık BLOB anahtar kullandığı için 8 baytlık tamsayı anahtardan hâlâ daha yavaş
  • UUID4 WITH ROWID, gizli rowid'nin sıralı yapısından yararlansa da iki indeksin getirdiği yazma amplifikasyonu ve rastgele indeks ekleme maliyeti nedeniyle UUID7 WITHOUT ROWID'den daha düşük performans gösterir

Kümelenmiş indeks nedir?

  • Kümelenmiş indeks, tablo satırlarının fiziksel depolama sırasını belirleyen indekstir
  • Satırlar fiziksel olarak yalnızca tek bir şekilde sıralanabildiğinden, her tabloda yalnızca bir kümelenmiş indeks bulunabilir
  • Kümelenmiş indeks tablonun kendisidir; kümelenmemiş indeks ise yalnızca indekslenen sütunları ve gerçek satır verisinin bulunduğu yeri gösteren işaretçileri depolar

Rowid

  • Tüm normal SQLite tabloları, rowid adlı örtük bir 64 bit tamsayı birincil anahtara sahiptir
  • Tablo verisi, her satır için bir giriş içeren B-tree yapısında saklanır ve anahtar olarak rowid değeri kullanılır
  • rowid, fiilen SQLite'ın kümelenmiş indeksidir ve satırların fiziksel depolama sırası rowid sırasıdır
Reklam

WITHOUT ROWID

  • SQLite, örtük rowid içermeyen WITHOUT ROWID tablolarını destekler
  • WITHOUT ROWID tablolarında, tanımlanan birincil anahtar kümelenmiş indeks görevi görür
  • SQLite rowid tabloları, tüm içeriğin yapraklarda saklandığı B*-Tree olarak uygulanırken, WITHOUT ROWID tabloları hem yaprak hem de ara düğümlerde içerik saklayan normal bir B-Tree kullanır

Taban çizgi: rowid tamsayı birincil anahtarı

  • Taban çizgi, id INTEGER PRIMARY KEY, data BLOB yapısına sahip normal bir rowid tablosunda 1 milyon satırlık ekleme sürelerinin ölçülmesiyle oluşturuldu
  • Sonuç tablosundaki toplam satır sayısı 10 milyon satırdan 100 milyon satıra kadar çıkıyor ve ölçüm süreleri 692ms ile 838ms arasında değişiyor
  • Taban çizgi performansı kabaca saniyede 1 milyon ekleme düzeyinde

UUID4 WITHOUT ROWID

  • UUID4 testi, id BLOB PRIMARY KEY, data BLOB yapısındaki bir WITHOUT ROWID tablosunda, birincil anahtar olarak random-uuid4-bytes değerleri eklenerek yapıldı
  • Sonuç tablosunda ölçüm süresi 10 milyon satırda 2649ms, 100 milyon satırda 12586ms olarak ölçüldü
  • Ekleme performansı, tamsayı rowid taban çizgisine göre 14-16 kat daha yavaş seviyede

Profil

  • Normalize edilmiş diffgraph, INTEGER ve UUID4 profil anlık görüntülerini karşılaştırır ve farkları flamegraph yapısında gösterir
  • Normalize görünüm, iki profilin toplam örnek sayısını eşitleyerek göreli farkları yüzde olarak görmeyi sağlar
  • Mavi çerçeveler, ikinci profil olan UUID4'te ilgili fonksiyon süresinin INTEGER'a göre azaldığını; kırmızı çerçeveler ise UUID4'te arttığını gösterir
  • Renk yoğunluğu, ilgili çerçevenin kendi örnek sayısındaki değişimi, yani self time delta'daki göreli değişimi ifade eder
  • Diffgraph'ta ağaç yeniden dengeleme ile okuma-yazmaya daha fazla zaman harcandığı görülür
  • UUID4'ün sırasız yapısı nedeniyle anahtarlar rastgele sırada yerleşir ve SQLite B-tree'yi sürekli yeniden dengeler
Reklam

UUID7 WITHOUT ROWID

  • UUID7, zaman sıralı bir UUID'dir ve UUID4'ün sıralama sorununu ortadan kaldırabilen bir yaklaşımdır
  • UUID7 testi de id BLOB PRIMARY KEY, data BLOB yapısındaki bir WITHOUT ROWID tablosunda çalıştırıldı
  • Sonuç tablosunda ölçüm süresi 10 milyon satırda 1372ms, 100 milyon satırda 1258ms olarak verildi
  • UUID7 WITHOUT ROWID, UUID4 WITHOUT ROWID'ye göre daha makul değerlere geri döndü; ancak taban çizgiden hâlâ daha yavaş kaldı
  • UUID BLOB birincil anahtarı 16 baytken, tamsayı birincil anahtar 8 bayttır

UUID4 WITH ROWID

  • UUID4 WITH ROWID testi, WITHOUT ROWID olmadan id BLOB PRIMARY KEY, data BLOB tablosu kullanılarak yapıldı
  • Bu yapılandırmada kümelenmiş indeks gizli rowid'dir ve rowid'nin avantajı sıralı olmasıdır
  • Dezavantaj ise tabloda iki indeks oluşması ve bunun getirdiği yazma amplifikasyonudur
  • Sonuç tablosunda ölçüm süresi 10 milyon satırda 2003ms, 100 milyon satırda 7119ms olarak ölçüldü
  • UUID4 WITH ROWID, UUID7 WITHOUT ROWID kadar iyi performans göstermedi; çünkü kümelenmiş indeks olmasa bile indeksin rastgele eklemelerle sürekli kurulması gerekiyor

Sonuç

  • SQLite'ta UUID birincil anahtarları, kümelenmiş indeks ve anahtarın sıralanabilirliği nedeniyle ekleme performansını ciddi biçimde etkileyebilen bir tercih olabilir
  • Rastgele UUID sorunu yalnızca SQLite'a özgü değil; kümelenmiş indeks kullanan diğer veritabanlarına da uzanan bir sorundur
  • Tüm benchmark kodu GitHub deposunda açık olarak yayımlandı

1 yorum

 
GN⁺ 4 시간 전
Lobste.rs görüşleri
  • Güzel. rowid tablolarda tamsayı anahtarın tekdüze artan değil de rastgele olduğu durumdaki sayıları da görmek ilginç olurdu
    Yazıda atlanan önemli nokta, rowid tabloların birincil indeksinin B+ ağacı, without rowid tabloların ise B ağacı olması
    Bu yüzden ortalama kayıt boyutu belli bir eşiği geçtiğinde ikincisi genelde ideal olmuyor. Çünkü indeksin iç düğümleri tüm kaydı saklıyor; diye hatırlıyorum, SQLite kılavuzu da sayfa boyutunun 1/20’sini pratik kural olarak veriyor

  • UUID performansını ölçmek için bu kadar emek harcanmış ama doğal anahtarlar hiç düşünülmemiş
    İster tamsayı, ister UUID, ister başka bir biçim olsun, surrogate key karmaşıklık ekler, bilgi eklemez ve normalizasyon hatalarını gizler
    Yazar, UUID kullanma nedeni olarak “yinelenmeleri önleme”yi gösteriyor ama bu bir gerekçe değil. Her veritabanındaki her tablodaki her satırın, o satırı benzersiz biçimde tanımlayan sütunlar kümesinden oluşan en az bir anahtarı olmalı
    Böyle bir anahtarı olmayan veritabanı yinelenen bilgi barındırır ve mantıksal tutarsızlıklara açıktır. Böyle bir anahtarın zaten bulunduğu veritabanında surrogate key gerekmez. Doğal anahtar zaten varsa ve zorunlu kılınıyorsa, “performans için gerekli” iddiası ek maliyet ve gereksiz karmaşıklık anlamına geldiğinden kanıtlanmalıdır

    • Belki hayal gücüm zayıftır ama gerçek dünyadan gelen verilerle çalışırken gerçek bir doğal anahtar bulmanın çoğu zaman zor olduğunu düşünüyorum
      Benzersiz görünen değerler beklendiği kadar benzersiz çıkmayabiliyor ya da değişmez sandığınız bir değerin sonunda değişmesi gerekebiliyor
      Buna karşılık surrogate key kullandığınızda, başkasının kimliği nasıl tanımladığına — ya da çoğu zaman düzgün tanımlamadığına — bağlı kalmadan kendi sisteminiz içinde kimliği tanımlayabiliyorsunuz
      İstisnalar var. Tüm modeli ben tanımlıyorsam ve veri sözde gerçek dünyadan gelmiyorsa doğal anahtar daha anlamlı olabilir. Ama muhtemelen hiçbir zaman tam normalize edilmemiş gerçek dünya verilerini içeren bir şema tasarlarken surrogate key çoğu zaman işe yarar
    • Tekdüze artan tamsayı surrogate key’lerin hemen görülen epey pratik faydası var
      Herhangi bir satıra tamsayıyla başvurabilmek erişimi büyük ölçüde basitleştiriyor; insanların aklında tutması ve sorgularda kullanması da kolay
      Tekdüze artıyorsa kendi başına bilgi de taşıyor. Yinelenen bilgi olsa da bu yine de doğru
      Arama performansı da optimize oluyor. B ağacı, bitmap vb. ile indekslemek için neredeyse ideal durum bu
      İnsanların UUID’yi çoğunlukla kafa karışıklığından kullandığını düşünüyorum. Genelde mantık, anahtarı gizlemek ve tahmin edilemez kılmak oluyor; ama bunun için ayrı bir tanımlayıcı yerine neden bunu birincil anahtara kadar dayatmak gerektiğini anlamaktan vazgeçtim
    • “Yinelenmeleri önleme” ifadesi blog yazısında geçmiyor. Aslında yazı UUID’lerin neden kullanıldığını hiç söylemiyor
  • UUID sürüm 7’de başta 48 bitlik bir zaman damgası var, bu yüzden bu şekilde rastgele dağılmıyor. Dolayısıyla aşırı sayfalama ve yeniden dengeleme de azalacaktır

    • Evet. Yazıda ele alınan UUID7
  • İnsanlar gerçekten UUID’yi birincil anahtar olarak mı kullanıyor? UUID gerektiğinde bunu ikincil anahtar olarak tutmak yerine böyle yapmanın ne avantajı var merak ediyorum

    • Ölçeklenme yüzünden. Çok sayıda bilgisayarda ve dünyanın farklı veri merkezlerinde yüksek hızda, örneğin S3 yüklemeleri gibi benzersiz kimlikleri dağıtık biçimde üretmek istiyorsanız tek bir artan tamsayıyı kilitlemek istemezsiniz. Kilitleme küresel ölçekte senkronizasyon için yavaştır
      GUID ve UUID bu sorunu yapıları gereği çözer
      v1 ve v6, makine kimliği ile zaman damgasını kodladığı için, makine başına ad alanı olan otomatik artan tamsayılara yakındır
      Karışıklığın kaynağı, birçok kişinin UUID’lerin rastgele olduğunu varsayması. Bu yalnızca v4 için geçerli ve ne yazık ki v4 seçiminin bir maliyeti var
      Verileri düzenlemek ya da performans ve çakışma garantileri için v3, v5, v7 gibi belli ölçüde deterministiklik gereken durumlar sık görülür
    • Rastgele UUID’yi ya da eşit dağılımlı rastgele değeri ikincil anahtarda tutsanız bile ekleme işlemi yine yavaşlar. Clustered index olmasa bile, o indeksin B ağacında yine rastgele eklemeler olur çünkü