bağlam
Dağıtılmış bir uygulamadan veri depolayacak bir veritabanı (PostgreSQL 9.6'da) tasarlıyorum. Uygulamanın dağıtılmış doğası SERIAL
nedeniyle, potansiyel artış koşulları nedeniyle otomatik artış tamsayılarını ( ) birincil anahtarım olarak kullanamıyorum.
Doğal çözüm, bir UUID veya global olarak benzersiz bir tanımlayıcı kullanmaktır. Postgres birlikte bir dahili UUID
tip mükemmel bir seçimdir.
UUID ile ilgili sorunum hata ayıklama ile ilgili: insan dostu olmayan bir dize. Tanımlayıcı ff53e96d-5fd7-4450-bc99-111b91875ec5
bana hiçbir şey söylemese ACC-f8kJd9xKCd
de, benzersiz olduğu garanti edilmese de, bir ACC
nesneyle uğraştığımı söylüyor .
Programlama açısından bakıldığında, birkaç farklı nesne ile ilgili uygulama sorgularında hata ayıklamak yaygındır. Programcının (sipariş) tablosunda yanlış bir ACC
(hesap) nesnesi aradığını varsayalım ORD
. İnsan tarafından okunabilen bir tanımlayıcıyla, programcı sorunu anında tanımlar, UUID'leri kullanırken neyin yanlış olduğunu anlamak için biraz zaman harcayacaktır.
UUID'lerin "garantili" benzersizliğine ihtiyacım yok; Ben do çakışma olmadan anahtarları oluşturmak için biraz yer gerekir, ancak UUID overkill. Ayrıca, en kötü durum senaryosu, bir çarpışma olursa dünyanın sonu olmazdı (veritabanı onu reddeder ve uygulama kurtarabilir). Dolayısıyla, değiş tokuşlar göz önüne alındığında, daha küçük ama insan dostu bir tanımlayıcı kullanım durumum için ideal çözüm olacaktır.
Uygulama nesnelerini belirleme
Geldiğim tanımlayıcı şu biçime sahip: {domain}-{string}
burada {domain}
nesne etki alanı (hesap, sipariş, ürün) ile değiştirilir ve {string}
rastgele oluşturulmuş bir dizedir. Bazı durumlarda, {sub-domain}
rastgele dizeden önce a eklemek bile mantıklı olabilir . Eşsizliği garanti etmek amacıyla {domain}
ve uzunluğunu göz ardı edelim {string}
.
Biçimlendirme / sorgulama performansına yardımcı oluyorsa biçim sabit bir boyuta sahip olabilir.
Sorun
Bilerek:
- Bir biçime sahip birincil anahtarlara sahip olmak istiyorum
ACC-f8kJd9xKCd
. - Bu birincil anahtarlar birkaç tablonun parçası olacaktır.
- Tüm bu anahtarlar 6NF veritabanında birçok birleşme / ilişki üzerinde kullanılacaktır.
- Çoğu tablo orta ila büyük-ish boyutuna sahip olacaktır (ortalama ~ 1M satır; en büyük olanlar ~ 100M satır).
Performansla ilgili olarak, bu anahtarı depolamanın en iyi yolu nedir?
Aşağıda dört olası çözüm bulunmaktadır, ancak veritabanlarıyla ilgili çok az deneyimim olduğu için hangisinin (hangisinin) en iyisi olduğundan emin değilim.
Dikkate alınan çözümler
1. dize olarak depola ( VARCHAR
)
(Postgres CHAR(n)
ve arasında hiçbir fark yaratmaz VARCHAR(n)
, bu yüzden görmezden geliyorum CHAR
).
Bazı araştırmalardan sonra VARCHAR
, özellikle birleştirme işlemlerinde dize karşılaştırmasının kullanmaktan daha yavaş olduğunu öğrendim INTEGER
. Bu mantıklı, ama bu ölçekte endişelenmem gereken bir şey mi?
2. İkili ( bytea
) olarak depola
Postgres'in aksine, MySQL'in yerel bir UUID
türü yoktur. Bir UUID'nin BINARY
36 baytlık VARCHAR
bir alan yerine 16 baytlık bir alan kullanarak nasıl saklanacağını açıklayan birkaç yazı vardır . Bu gönderiler bana anahtarı ikili ( bytea
Postgres'te) olarak saklama fikrini verdi .
Bu boyut tasarrufu sağlar, ancak performansla daha fazla ilgileniyorum. İkili veya dize olanlar: karşılaştırma daha hızlı bir açıklama bulmak için biraz şans vardı. İkili karşılaştırmanın daha hızlı olduğuna inanıyorum. Eğer öyleyse bytea
, VARCHAR
programcı şimdi her zaman verileri kodlamak / kodunu çözmek zorunda olsa bile , muhtemelen daha iyidir .
Yanlış olabilirim, ama her ikisini de düşünüyorum bytea
ve VARCHAR
(eşitlik) bayt bayt (veya karakter karakter) karşılaştırır. Bu adım adım karşılaştırmayı "atlamanın" ve basitçe "her şeyi" karşılaştırmanın bir yolu var mı? (Ben öyle düşünmüyorum, ama kontrol etmenin maliyeti yok).
Depolamayı bytea
en iyi çözüm olarak düşünüyorum , ama görmezden geldiğim başka alternatifler olup olmadığını merak ediyorum. Ayrıca, çözüm 1'de ifade ettiğim aynı endişe de geçerli: karşılaştırmalardaki yük endişe etmem yeterli mi?
"Yaratıcı çözümler
Çalışabilecek iki çok "yaratıcı" çözüm buldum, sadece ne ölçüde emin değilim (yani bir tablodaki birkaç binden fazla satıra ölçeklendirme konusunda sorun yaşarsam).
3. UUID
"Etiketi" yapıştırılmış olarak ancak depolayın
UUID'lerin kullanılmamasının ana nedeni, programcıların uygulamada daha iyi hata ayıklaması yapabilmesidir. Ancak her ikisini de kullanabilirsek: veritabanı tüm anahtarları UUID
yalnızca s olarak depolar , ancak sorgular yapılmadan önce / sonra nesneyi sarar.
Örneğin, programcı ister ACC-{UUID}
, veritabanı ACC-
parçayı yok sayar , sonuçları getirir ve tümünü olarak döndürür {domain}-{UUID}
.
Belki de bu, saklı yordamlar veya işlevlere sahip bazı bilgisayar korsanları ile mümkün olabilir, ancak bazı sorular akla geliyor:
- Bu (her sorguda alan adının kaldırılması / eklenmesi) önemli bir ek yük mü?
- Bu mümkün mü?
Daha önce saklı yordamları veya işlevleri hiç kullanmadım, bu yüzden bunun mümkün olup olmadığından emin değilim. Birisi biraz ışık tutabilir mi? Programcı ve saklanan veriler arasına şeffaf bir katman ekleyebilirsem, mükemmel bir çözüm gibi görünüyor.
4. (Favori) IPv6 olarak depola cidr
Evet, doğru okudunuz. IPv6 adres formatının sorunumu mükemmel bir şekilde çözdüğü ortaya çıktı .
- İlk birkaç sekizlide etki alanları ve alt etki alanları ekleyebilir ve kalanları rastgele dize olarak kullanabilirim.
- Çarpışma oran Tamam. (Ben olsa 2 ^ 128 kullanarak olmazdı, ama yine de sorun yok.)
- Eşitlik karşılaştırmaları (umarım) optimize edilmiştir, bu yüzden sadece kullanmaktan daha iyi performans elde edebilirim
bytea
. contains
Etki alanlarının ve hiyerarşilerinin nasıl temsil edildiğine bağlı olarak bazı ilginç karşılaştırmalar yapabilirim .
Örneğin, 0000
"ürünler" alanını temsil etmek için kod kullandığımı varsayalım . Anahtar 0000:0db8:85a3:0000:0000:8a2e:0370:7334
ürünü temsil eder 0db8:85a3:0000:0000:8a2e:0370:7334
.
Buradaki ana soru şudur: veri türünü bytea
kullanmanın herhangi bir temel avantajı veya dezavantajı var cidr
mı?
varchar
birçok problem arasında olacaktır . PG'nin alanlarını bilmiyordum, bu da öğrenmesi harika. Belirli bir sorgu doğru nesneyi kullanıyorsa doğrulamak için kullanılan etki alanları görüyorum, ancak yine de tamsayı olmayan bir dizin olması güveniyor. serial
Burada "güvenli" bir yol olup olmadığından emin değilsiniz (tek bir kilit adımı olmadan).
varchar
. Bunu bir FK
integer
tür yapmayı düşünün ve bunun için bir arama tablosu ekleyin. Bu şekilde hem insan tarafından okunabilir olabilirsiniz hem de kompozitinizi PK
ekleme / güncelleme anormalliklerinden (var olmayan bir alan adı koyarak) koruyabilirsiniz .
text
tercih edilir varchar
. Bak depesz.com/2010/03/02/charx-vs-varcharx-vs-varchar-vs-text ve postgresql.org/docs/current/static/datatype-character.html
ACC-f8kJd9xKCd
. ”← Bu eski iyi kompozit PRIMARY KEY için bir iş gibi görünüyor .