MD5 alanı için en uygun veri türü nedir?


35

Ağır okuma olduğu bilinen bir sistemi tasarlıyoruz (dakikada on binlerce okuma sırasına göre).

  • namesBir çeşit merkezi kayıt defteri görevi gören bir tablo var. Her satırın bir textalanı representationve bunun keybir MD5 karması olan benzersiz bir değeri vardır representation. 1 Bu tablo şu anda on milyonlarca kayda sahiptir ve uygulamanın ömrü boyunca milyarlarca büyümesi beklenmektedir.
  • Tabloya referans veren düzinelerce başka tablolar (çok çeşitli şemalar ve kayıt sayısı) vardır names. Bu tablolardan birindeki herhangi bir kaydın name_key, işlevsel olarak namesmasanın yabancı bir anahtarı olan bir a olması garanti edilmektedir .

1: Bu arada, beklediğiniz gibi, bu tablodaki kayıtlar bir kez yazılmaz.

Tablodan başka herhangi bir tablo için names, en yaygın sorgu bu deseni izleyecektir:

SELECT list, of, fields 
FROM table 
WHERE name_key IN (md5a, md5b, md5c...);

Okuma performansı için optimize etmek istiyorum. İlk durağımın endekslerin büyüklüğünü en aza indirmek olması gerektiğinden şüpheleniyorum (orada yanlış olduğunu ispat etmeme rağmen).

Soru: ve sütunları
için en uygun veri tipleri nelerdir / nelerdir ? Kullanmak için bir neden var mı üzerinde ? ya ?keyname_key
hex(32)bit(128)BTREEGIN

Yanıtlar:


41

Veri türü uuidolan mükemmel görev için uygundur. RAM'de temsil varcharveya texttemsil için 37 byte'a karşılık yalnızca 16 byte kaplar . (Veya diskte 33 bayt, ancak tek sayı, etkili bir şekilde 40 bayt yapmak için çoğu durumda doldurmayı gerektirir .) Ve uuidtürün biraz daha avantajları vardır.

Örnek:

SELECT md5('Store hash for long string, maybe for index?')::uuid AS md5_hash

Ayrıntılar ve daha fazla açıklama:

Eğer md5'in kriptografik bileşenine ihtiyacınız yoksa, diğer (daha ucuz) karma fonksiyonları düşünebilirsiniz, ancak kullanım durumunuz için md5 ile giderdim (çoğunlukla salt okunur).

Bir uyarı : Davanız için ( immutable once written) işlevsel olarak bağımlı (sözde doğal) bir PK iyidir. Ancak aynı şey güncellemelerin mümkün olduğu bir acı olacaktır text. Bir yazım hatası düzeltmeyi düşünün: PK ve tüm bağımlı dizinler, FK sütunları dozens of other tablesve diğer referanslar da değişmek zorunda kalacak. Tablo ve dizin şişirme, kilitleme sorunları, yavaş güncellemeler, kaybedilen referanslar, ...

Eğer textnormal çalışma içinde değiştirebilir, bir vekil PK daha iyi bir seçim olacaktır. Ben bir bigserialsütun (aralık -9223372036854775808 to +9223372036854775807- bu dokuz beş milyon iki yüz yirmi üç katrilyon üç yüz yetmiş iki trilyon otuz altı şey milyar bir ) öneririm billions of rows. Her durumda iyi bir fikir olabilir : düzinelerce FK sütun ve dizin için 16 bayt yerine 8 !). Veya çok daha büyük kardinaliteler veya dağıtılmış sistemler için rastgele bir UUID . Ana tablodaki satırları orijinal metinden hızlı bir şekilde bulmak için her zaman sözü edilen md5'i (as ) da ek olarak saklayabilirsiniz . İlgili:uuid

Sorgunuz gelince :


@ Daniel'ın yorumuna hitap etmek için : Tiresiz bir temsili tercih ederseniz, ekran için tireleri kaldırın:

SELECT replace('90b7525e-84f6-4850-c2ef-b407fae3f271', '-', '')

Ama canını sıkmazdım. Varsayılan gösterim gayet iyi. Ve sorun gerçekten burada temsil değil.

Diğer tarafların farklı bir yaklaşıma sahip olmaları ve karışıma tire koymadan dizeleri atmaları gerekiyorsa, bu da sorun olmaz. Postgres'ler, a için girdi olarak birkaç makul metin gösterimini kabul eder uuid. Belgeler :

PostgreSQL ayrıca girdi için aşağıdaki alternatif formları da kabul eder: büyük harfli rakamların kullanılması, parantezlerle çevrili standart format, kısa veya küçük tire, ihmal, dört basamaklı herhangi bir gruptan sonra tire eklemek. Örnekler:

A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11
{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}
a0eebc999c0b4ef8bb6d6bb9bd380a11
a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11
{a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}

Dahası, md5()fonksiyon döner textşunları kullanırsınız decode()dönüştürmek byteave varsayılan gösterimi şu geçerli:

SELECT decode(md5('Store hash for long string, maybe for index?'), 'hex')

\220\267R^\204\366HP\302\357\264\007\372\343\362q

encode()Orijinal metin sunumunu almak için tekrar yapmanız gerekir :

SELECT encode(my_md5_as_bytea, 'hex');

Baştan sona , özellikle basit indekslerin boyut ve performansı için elverişsiz olan yük nedeniyle byteaRAM'de 20 bayt (ve diskte 17 bayt, dolgulu 24 bayt) kaplayacak şekilde depolanır .varlena

Her şeyuuid burada bir lehine çalışıyor .


1
Bu "uuid" için yasal mı? Lütfen çok bilgili olduğum için afedersiniz, ama gördüğüm şey "uuid" veri türünün, 16 oktet uzunluğundaki sayıları ikili biçimde saklamaya yönelik olmasıdır. Ancak "uuid" terimi, belirli bir üretim / karma algoritma ile birlikte 5 satırla ayrılmış onaltılık karakterlerden oluşan geleneksel metin gösterimi anlamına gelir. Bu tür ad, UUID / GUID nesnesini şiddetle önerirse, programcılar için en azından bir hash depolamak için bu türü kullanmak biraz yanıltıcı olmaz mı?
Andrew Wolfe,

2
@AndrewWolfe: Tamamen okunaklı, IMO. İsme göre taşınma . Sağlanan bir dizi tip yayın ve giriş / çıkış mantığı ile 16 baytlık bir varlıktır. Eldeki durum aslında bir "benzersiz tanımlayıcı" gerektiriyor. Her türlü karakter verisini textsütunlarda da saklayabilirsiniz - hiç bir "metin" olmasa bile.
Erwin Brandstetter

MD5 karma 64'e dönüştürülürse, peki bunu nasıl saklayacaksınız
PirateApp

2
@PirateApp, ilk önce deşifre: SELECT encode(decode('tZmffOd5Tbh8yXaVlZfRJQ==', 'base64'), 'hex')::uuid;.
nyov

1
@nyov: uuid160 ila 512 bit üreten herhangi bir SHA algoritmasının sonuçlarını saklayamayan 16 baytlık bir türdür. Postgres'in standart dağılımına uyan benzer bir tip yoktur. Bir tane oluşturabilirsiniz ... Başarısızlık, varsayılan bytea- gibi pg_crypto yapar.
Erwin Brandstetter

2

MD5'i bir textveya varcharsütunda saklardım. Çeşitli karakter veri tipleri arasında performans farkı yoktur. varchar(xxx)Md5 değerinin hiçbir zaman belirli bir uzunluğu aşmadığından emin olmak için kullanarak md5 değerlerinin uzunluğunu sınırlamak isteyebilirsiniz .

Büyük IN listeleri genellikle hızlı değildir, böyle bir şey yapmak daha iyidir:

with md5vals (md5) as (
  values ('one'), ('two'), ('three')
)
select t.*
from the_table t
  join md5vals m on t.name_key  = m.md5;

Bazen daha hızlı olduğu söylenen başka bir seçenek bir dizi kullanmaktır:

select t.*
from the_table t
where name_key = ANY (array['one', 'two', 'three']);

Sadece eşitliği karşılaştırırken, düzenli bir BTree endeksi iyi olmalıdır. Her iki sorgu da, bu tür bir dizini kullanabilmelidir (özellikle satırların sadece küçük bir kısmını seçiyorsanız).


Bit (128) veya hex (32) kullanmamak için herhangi bir sebep var mı? Değerlerin böyle bir alana tam olarak oturması garanti edilir ve atanan hatalı değerlerden korunmak isterim.
bobocopy

3
@bocopy: Postgres'te "hex" veri türü yok. Ben hiç bir zaman bu bittip kullanmıyorum, bu yüzden bu konuda yorum yapamam. Satır beklenen sayı göz önüne alındığında, Erwin'ın öneri UUID olarak bu depolama ile olsun tasarruf daha iyi çünkü boşluk gibi görünüyor
a_horse_with_no_name

-1

Diğer bir seçenek ise 4 INTEGER veya 2 BIGINT kolon kullanmaktır.


2
Depolama boyutu açısından, her iki seçenek de elbette uygun olacaktır, ancak bununla çalışmak ne kadar kolay olurdu? Belki bir örnek göstermek veya cevabını açıklamak için cevabınızı genişletebilirsiniz.
Andriy M
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.