Veritabanları değişken uzunluklu alanlar için dizin anahtarı değerlerini (disk üzerinde) nasıl depolar?


16

bağlam

Bu soru, SQL ve NoSQL veritabanı sistemlerindeki dizinlerin düşük düzeyli uygulama ayrıntıları ile ilgilidir. Endeksin gerçek yapısı (B + ağacı, hash, SSTable, vb.), Soru özellikle bu uygulamalardan herhangi birinin tek bir düğümünde saklanan anahtarlarla ilgili olduğundan, önemsizdir .

Arka fon

Aslında yapmak veritabanını neden oluyor ne bir sütun veya veri JSON belge alanında bulunan bir dizin oluşturmak SQL (örneğin MySQL) ve NoSQL (CouchDB, MongoDB, vs.) veritabanları, In isimli oluşturmak esasen tüm sıralı bir liste bu değerlerle birlikte bir dosya ile birlikte bu değere ait kaydın yaşadığı ana veri dosyasına kaydırılır.

(Basitlik uğruna, belirli impls'ların diğer ezoterik ayrıntılarını elle sallıyor olabilirim)

Basit Klasik SQL Örneği

Bir dizin oluşturduğumuz basit bir 32-bit int birincil anahtara sahip standart bir SQL tablosunu düşünün, veri dosyasında 64 bitlik bir ofset ile sıralanan ve ilişkilendirilen tamsayı anahtarlarının bir diskteki diziniyle sonuçlanacağız. kayıt yaşıyor, örneğin:

id   | offset
--------------
1    | 1375
2    | 1413
3    | 1786

Dizindeki anahtarların disk üzerinde gösterimi şuna benzer:

[4-bytes][8-bytes] --> 12 bytes for each indexed value

Dosya sistemleri ve veritabanı sistemleri ile disk G / Ç'sini optimize etme konusunda standart kurallara bağlı kalarak, anahtarları diskte 4KB bloklarında sakladığınızı varsayalım:

4096 bytes / 12 bytes per key = 341 keys per block

Endeksin genel yapısını göz ardı ederek (B + ağacı, karma, sıralı liste vb.) 341 tuş bloklarını bir kerede belleğe okur ve yazar ve gerektiğinde diske geri veririz.

Örnek Sorgu

Önceki bölümdeki bilgileri kullanarak, "id = 2" için bir sorgu geldiğini varsayalım, klasik DB dizin araması aşağıdaki gibi gider:

  1. Dizinin kökünü okuyun (bu durumda 1 blok)
  2. Anahtarı bulmak için sıralanmış bloğa ikili arama yapın
  3. Veri dosyası değerini değerden alın
  4. Ofseti kullanarak veri dosyasındaki kaydı arama
  5. Verileri arayana geri döndürme

Soru Ayarı ...

Tamam, burada soru bir araya geliyor ...

Adım # 2, bu sorguların O (logn) zamanında yürütülmesine izin veren en önemli bölümdür ... bilgilerin sıralanması gerekir, ancak listeyi hızlı bir şekilde gezebilmeniz gerekir ... daha fazla özellikle, bu konumdaki dizin anahtarı değerini okumak için iyi tanımlanmış ofsetlere atlayabilmeniz gerekir.

Blokta okuduktan sonra, hemen 170. konuma atlayabilmeniz, anahtar değerini okuyabilmeniz ve aradığınız şeyin bu konum GT veya LT olup olmadığını görmeniz gerekir (vb. Vb.)

Bloktaki verilerin etrafında bu şekilde atlayabilmenin tek yolu, anahtar örnek boyutlarının yukarıdaki örnekteki gibi iyi tanımlanmış olması (4 bayt sonra anahtar başına 8 bayt).

SORU

Tamam, işte burada verimli dizin tasarımı ile sıkışıp kalıyorum ... SQL veritabanlarındaki varchar sütunları veya daha spesifik olarak, CouchDB veya NoSQL gibi belge veritabanlarındaki tamamen serbest biçimli alanlar için, indekslemek istediğiniz herhangi bir alanın length Endekslerinizi oluşturduğunuz dizin yapısının bloklarının içindeki anahtar değerleri nasıl uygularsınız?

Örneğin, CouchDB'deki bir kimlik için sıralı bir sayaç kullandığınızı ve tweet'leri dizine eklediğinizi varsayalım ... birkaç ay sonra "1" ile "100.000.000.000" arasında değişen değerleriniz olacak.

Dizin, veritabanında yalnızca 4 tweet olduğunda 1. günde veritabanında oluşturduğunuzu varsayalım, CouchDB, dizin bloklarının içindeki anahtar değerler için aşağıdaki yapıyı kullanmaya cazip gelebilir:

[1-byte][8-bytes] <-- 9 bytes
4096 / 9 = 455 keys per block

Bir noktada bu kesilir ve anahtar değerinizi dizinlerde saklamak için değişken sayıda bayt gerekir.

Eğer "tweet_message" ya da başka bir şey gibi değişken uzunluktaki bir alanı endekslemeye karar verirseniz, bu nokta daha da göze çarpıyor.

Anahtarın kendisinin tamamen değişken uzunluğu olması ve veritabanının dizin oluşturulduğunda ve güncellendiğinde bazı "maksimum anahtar boyutunda" akıllıca tahmin etmesinin bir yolu olmadığında, bu anahtarlar aslında bu veritabanlarındaki dizinlerin segmentlerini temsil eden bloklar içinde nasıl depolanır? ?

Anahtarlarınız değişken boyutluysa ve bir anahtar bloğunda okuduysanız, sadece blokta gerçekten kaç anahtar olduğunu bilmiyorsunuz , aynı zamanda bir ikili yapmak için listenin ortasına nasıl atlayacağınız hakkında hiçbir fikriniz yok. Onları araştırın.

Burası benim açtığım yer.

Klasik SQL veritabanlarındaki statik olarak yazılan alanlarla (bool, int, char vb.) Endeksin anahtar uzunluğunu önceden tanımlayabildiğini ve ona yapışabileceğini anlıyorum ... ancak bu belge veri depoları dünyasında, bu verileri diskte nasıl verimli bir şekilde modellediklerini, O (logn) zamanda hala taranabileceklerini ve burada herhangi bir açıklamanın takdir edileceğini şaşırttı.

Herhangi bir açıklama gerekiyorsa lütfen bana bildirin!

Güncelleme (Greg'in Cevabı)

Lütfen Greg'in cevabına eklenen yorumlarıma bakın. Bir hafta daha süren araştırmalardan sonra, umursadığınız önemli değerlerin serileştirilmesinden kaçınmak için büyük performans kazançları sağlarken, uygulamada uygulanması ve kullanımı son derece kolay ve mükemmel bir öneri üzerine tökezlediğini düşünüyorum.

3 ayrı DBMS uygulaması (CouchDB, kivaloo ve InnoDB) ve hepsi , yürütme ortamındaki değerleri (erlang / C) aramadan önce tüm blok iç veri yapısı içine serileştirerek bu sorunu ele.

Bu Greg'in önerisi hakkında çok parlak olduğunu düşünüyorum; 2048 normal blok boyutunun normalde 50 veya daha az ofseti olacaktır ve bu da okunması gereken çok küçük bir sayı bloğuyla sonuçlanır.

Güncelleme (Greg'in Tavsiyesinde Potansiyel Dezavantajlar)

Bu diyaloğa en iyi şekilde devam edebilmek için, bunun aşağıdaki dezavantajlarını fark ettim ...

  1. Her bir "blok" ofset verileri ile yönlendirilirse, blok boyutunun doğru bir şekilde başlıkla veya bir blokla başlamayan verilerde okunabileceği için konfigürasyonda yolun ilerleyen kısımlarında ayarlanmasına izin veremezdiniz. birden çok başlık içeriyordu.

  2. Büyük anahtar değerlerini endeksliyorsanız (birisinin char (8192) veya blob (8192) sütununu dizine eklemeye çalıştığını varsayalım), anahtarların tek bir bloğa sığmaması ve iki blok boyunca yan yana taşması gerekebilir . Bu, ilk bloğunuzun ofset üstbilgisine sahip olacağı ve ikinci bloğun hemen anahtar verilerle başlayacağı anlamına gelir.

Tüm bu çözüm sabit bir veritabanı blok boyutuna sahip olan değil ayarlanabilir ve çevresinde başlık blok veri yapılarını geliştirerek ... Örneğin, 4KB'a tüm blok boyutları (zaten genellikle en optimal olan) düzeltmek ve çok küçük bir yazma başında "blok türü" içeren blok başlığı. Normal bir blok ise, blok başlığından hemen sonra ofset başlığı olmalıdır. Bu bir "taşma" türü ise, blok başlığından hemen sonra ham anahtar verisidir.

Güncelleme (Potansiyel harika yukarı taraf)

Blok bir dizi bayt olarak okunduktan ve ofsetlerin kodu çözüldükten sonra; teknik olarak sadece aradığınız anahtarı ham baytlara kodlayabilir ve daha sonra bayt akışında doğrudan karşılaştırmalar yapabilirsiniz.

Aradığınız anahtar bulunduğunda, imlecin kodu çözülebilir ve takip edilebilir.

Greg'in fikrinin bir başka harika yan etkisi! Buradaki CPU zaman optimizasyonu potansiyeli, sabit bir blok boyutu ayarlamanın tüm bunları elde etmek için buna değecek kadar büyüktür.


Bu konuyla ilgilenen herkes için Redis'in öncü geliştirmesi, Redis için geçersiz "disk deposu" bileşenini uygulamaya çalışırken bu sorunla karşılaşıyordu. Başlangıçta 32 baytlık "yeterince büyük" bir statik anahtar boyutu seçti, ancak sorunların potansiyelini fark etti ve bunun yerine, tutarlı bir boyuta sahip olmak için anahtarların karmasını (sha1 veya md5) saklamayı seçti. Bu, aralıklı sorgular yapma yeteneğini öldürür, ancak ağacı güzelce FWIW dengeliyor. Ayrıntılar burada redis.hackyhack.net/2011-01-12.html
Riyad Kalla

Biraz daha bilgi buldum. SQLite, anahtarların ne kadar büyük olabileceğine dair bir kapağa sahip gibi görünüyor veya aslında bazı üst sınırlarda anahtar değerini kısaltıyor ve geri kalanını diskte bir "taşma sayfasına" koyuyor. Bu, rasgele g / Ç iki katına çıktıkça büyük anahtarların sorgularını korkunç kılabilir. Buradaki "B-ağacı sayfaları" bölümüne gidin sqlite.org/fileformat2.html
Riyad Kalla

Yanıtlar:


7

Ana verilerinizi içeren bloğa dizininizi sabit boyutlu ofsetlerin bir listesi olarak saklayabilirsiniz. Örneğin:

+--------------+
| 3            | number of entries
+--------------+
| 16           | offset of first key data
+--------------+
| 24           | offset of second key data
+--------------+
| 39           | offset of third key data
+--------------+
| key one |
+----------------+
| key number two |
+-----------------------+
| this is the third key |
+-----------------------+

(anahtar veriler gerçek bir örnekte sıralanır, ancak fikri anlarsınız).

Bunun, dizin bloklarının herhangi bir veritabanında gerçekte nasıl oluşturulduğunu yansıtması gerekmediğini unutmayın . Bu sadece nasıl bir örnektir olabilir anahtar veri değişken uzunlukta olup endeks veri bloğunu düzenler.


Greg, cevabınızı henüz defacto yanıtı olarak seçmedim çünkü diğer DBMS'lere biraz daha fazla araştırma yapmanın yanı sıra daha fazla araştırma yapmayı umuyorum (orijinal Q'ya yorumlarımı ekliyorum). Şimdiye kadar en yaygın yaklaşım bir üst sınır kapağı ve daha sonra sadece tam anahtar gerektiğinde kontrol edilen bir taşma tablosundaki anahtarın geri kalanı gibi görünüyor. O kadar zarif değil. Çözümünüzün hoşuma giden bir zarafeti var, ancak tuşların sayfa boyutumuzu havaya uçurduğu uç durumda, yolunuz hala bir taşma tablosuna ihtiyaç duyacak veya buna izin vermeyecek.
Riyad Kalla

Ben boş yer kalmadı ... Kısacası db tasarımcı anahtar boyutu bazı zor sınırları ile yaşayabilir, ben yaklaşımınız en verimli ve esnek olduğunu düşünüyorum. Uzay ve cpu verimliliği Nice combo. Taşma tabloları daha esnektir, ancak sürekli taşan anahtarlar için rasgele i / o eklemek için huzursuz olabilir. Bu konudaki bilgiler için teşekkürler!
Riyad Kalla

Greg, bunu daha fazla düşünüyor, alternatif çözümlere bakıyorum ve bence ofset başlık fikrini çiviledin. Bloklarınızı küçük tuttuysanız, 8 bitlik (1 bayt) ofsetlerle kurtulabilirsiniz, daha büyük bloklarla 16 bit, makul olması gereken 128KB veya 256KB bloklara kadar bile en güvenli olurdu (4 veya 8 bayt anahtar varsayalım). Büyük kazanç, ofset verilerinde ne kadar ucuz ve hızlı okuyabileceğiniz ve sonuç olarak ne kadar serileştirmeden tasarruf edeceğinizdir. Mükemmel öneri, tekrar teşekkür ederim.
Riyad Kalla

Bu, aynı zamanda UpscaleDB kullanılan yaklaşımdır upscaledb.com/about.html#varlength
Mathieu Rodic
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.