MD5 फ़ील्ड के लिए इष्टतम डेटा प्रकार क्या है?


35

हम एक ऐसी प्रणाली तैयार कर रहे हैं, जो पढ़ने में भारी (प्रति मिनट हजारों की संख्या में रीड्स के आदेश पर) जानी जाती है।

  • एक मेज है namesजो केंद्रीय रजिस्ट्री की तरह काम करती है। प्रत्येक पंक्ति में एक textफ़ील्ड representationऔर एक विशिष्ट है keyजो कि MD5 हैश है representation1 इस तालिका में वर्तमान में लाखों रिकॉर्ड हैं और उम्मीद है कि यह जीवन भर अरबों में विकसित होगी।
  • दर्जनों अन्य टेबल (अत्यधिक भिन्न स्कीमा और रिकॉर्ड काउंट्स) हैं जो namesतालिका का संदर्भ देते हैं । इन तालिकाओं में से किसी एक में दिए गए रिकॉर्ड की गारंटी दी जाती है name_key, जो कार्यात्मक रूप से namesतालिका के लिए एक विदेशी कुंजी है ।

1: संयोग से, जैसा कि आप उम्मीद कर सकते हैं, एक बार लिखे गए इस तालिका में रिकॉर्ड अपरिवर्तनीय हैं।

तालिका के अलावा किसी भी दी गई तालिका के namesलिए, सबसे सामान्य क्वेरी इस पैटर्न का अनुसरण करेगी:

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

मैं पठन प्रदर्शन के लिए अनुकूलन करना चाहूंगा। मुझे संदेह है कि मेरा पहला पड़ाव सूचकांकों के आकार को कम से कम करना चाहिए (हालांकि मेरा मन नहीं होगा कि वहां गलत साबित हो)।

प्रश्न:
क्या keyऔर name_keyस्तंभों के लिए इष्टतम डेटा प्रकार क्या है ?
वहाँ का उपयोग करने के लिए एक कारण है hex(32)के ऊपर bit(128)? BTREEया GIN?

जवाबों:


41

डेटा प्रकार uuidहै पूरी तरह से कार्य के लिए उपयुक्त है। यह केवल 16 बाइट्स पर कब्जा कर लेता है क्योंकि RAM में 37 बाइट्स का विरोध varcharया textप्रतिनिधित्व होता है। (या डिस्क पर 33 बाइट्स, लेकिन विषम संख्या को प्रभावी रूप से 40 बाइट्स बनाने के लिए कई मामलों में पैडिंग की आवश्यकता होगी ।) और इस uuidप्रकार के कुछ और फायदे हैं।

उदाहरण:

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

विवरण और अधिक विवरण:

यदि आपको md5 के क्रिप्टोग्राफिक घटक की आवश्यकता नहीं है, तो आप अन्य (सस्ते) हैशिंग कार्यों पर विचार कर सकते हैं, लेकिन मैं आपके उपयोग के मामले में md5 के साथ जाऊंगा (ज्यादातर केवल पढ़ने के लिए)।

चेतावनी का एक शब्द : आपके मामले के लिए ( immutable once written) एक कार्यात्मक रूप से निर्भर (छद्म प्राकृतिक) पीके ठीक है। लेकिन वही दर्द होगा जहां अपडेट textसंभव है। एक टाइपो को ठीक करने के बारे में सोचें: पीके और सभी निर्भरता सूचकांक, एफके कॉलम dozens of other tablesऔर अन्य संदर्भों में भी बदलना होगा। टेबल और इंडेक्स ब्लोट, लॉकिंग इश्यू, धीमे अपडेट, रेफरेंस खो ...

यदि textसामान्य ऑपरेशन में बदल सकते हैं, तो एक सरोगेट पीके एक बेहतर विकल्प होगा। मेरा सुझाव है कि एक bigserialकॉलम (रेंज -9223372036854775808 to +9223372036854775807- वह नौ क्विंटल दो सौ तेईस क्वाड्रिलियन तीन सौ बहत्तर खरब छत्तीस बिलियन बिलियन ) के लिए अलग - अलग मान है billions of rowsकिसी भी मामले में एक अच्छा विचार हो सकता है : दर्जनों एफके कॉलम और इंडेक्स के लिए 16 बाइट्स के बजाय 8 !)। या बहुत बड़ी कार्डिनैलिटी या वितरित प्रणालियों के लिए एक यादृच्छिक यूयूआईडी । आप हमेशा मूल पाठ से मुख्य तालिका में पंक्तियों को खोजने के लिए md5 (as ) को अतिरिक्त रूप से संग्रहीत कर सकते हैं । सम्बंधित:uuid

अपनी क्वेरी के लिए :


@ डैनियल की टिप्पणी को संबोधित करने के लिए : यदि आप हाइफ़न के बिना एक प्रतिनिधित्व पसंद करते हैं, तो प्रदर्शन के लिए हाइफ़न निकालें:

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

लेकिन मैं परेशान नहीं होता। डिफ़ॉल्ट प्रतिनिधित्व ठीक है। और समस्या वास्तव में यहाँ प्रतिनिधित्व नहीं है।

यदि अन्य पार्टियों में एक अलग दृष्टिकोण होना चाहिए और मिश्रण में हाइफ़न के बिना तार फेंकना चाहिए, तो यह कोई समस्या नहीं है। Postgres के लिए इनपुट के रूप में कई उचित पाठ अभ्यावेदन स्वीकार करता है uuidप्रलेखन :

PostgreSQL इनपुट के लिए निम्नलिखित वैकल्पिक रूपों को भी स्वीकार करता है: ऊपरी-केस अंकों का उपयोग, ब्रेसिज़ से घिरा मानक प्रारूप, कुछ या सभी हाइफ़न को छोड़ कर, चार अंकों के किसी भी समूह के बाद एक हाइफ़न जोड़ना। उदाहरण हैं:

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

क्या अधिक है, md5()फ़ंक्शन वापस आता है text, आप इसका उपयोग decode()करने के लिए byteaऔर उस का डिफ़ॉल्ट प्रतिनिधित्व करने के लिए करेंगे :

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

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

आपको encode()मूल पाठ प्रतिनिधित्व प्राप्त करने के लिए फिर से करना होगा :

SELECT encode(my_md5_as_bytea, 'hex');

इसे बंद करने के लिए, byteaरैम में 20 बाइट्स (और डिस्क पर 17 बाइट्स, 24 पेडिंग के साथ ) के रूप में संग्रहीत मान आंतरिक varlenaओवरहेड के कारण होता है, जो आकार और सरल अनुक्रमित के प्रदर्शन के लिए विशेष रूप से प्रतिकूल है।

सब कुछuuid यहाँ के पक्ष में काम करता है।


1
क्या यह "uuid" के लिए वैध है? कृपया मुझे क्षमा करें यदि मैं बहुत अधिक पंडित हूं, लेकिन मुझे लगता है कि जो मैं देख रहा हूं वह यह है कि "यूआईडी" डेटा प्रकार उन संख्याओं को संग्रहीत करने के लिए उन्मुख है जो बाइनरी प्रारूप में लंबाई में 16 ओकटेट हैं। लेकिन "यूआईडी" शब्द एक विशेष पीढ़ी / हैशिंग एल्गोरिथ्म के साथ-साथ डैश-अलग-अलग हेक्साडेसिमल वर्णों के 5 ब्लॉकों में पारंपरिक पाठात्मक प्रतिनिधित्व का सुझाव देता है। यदि इस प्रकार का नाम UUID / GUID पीढ़ी का दृढ़ता से सुझाव देता है, तो क्या यह प्रोग्रामर के लिए कम से कम, इस प्रकार का हैश के भंडारण के लिए उपयोग करने के लिए, यह भ्रामक नहीं है?
एंड्रयू वोल्फ

2
@AndrewWolfe: पूरी तरह से कानूनी, IMO। नाम से दूर मत करो । यह एक 16-बाइट इकाई है जो प्रदान किए गए प्रकारों और इनपुट / आउटपुट लॉजिक के सुविधाजनक सेट के साथ है। हाथ में मामला भी वास्तव में एक "अद्वितीय पहचानकर्ता" की आवश्यकता है। आप सभी प्रकार के वर्ण डेटा को textकॉलम में संग्रहीत कर सकते हैं - भले ही यह "पाठ" बिल्कुल भी न हो।
इरविन ब्रान्डस्टेट्टर ५

क्या होगा अगर एमडी 5 हैश को बेस 64 में बदल दिया जाता है, तो आप इसे कैसे स्टोर करेंगे
PirateApp

2
@PirateApp, इसे पहले डीकोड करें SELECT encode(decode('tZmffOd5Tbh8yXaVlZfRJQ==', 'base64'), 'hex')::uuid;:।
नीव

1
@nyov: uuidएक 16-बाइट प्रकार है जो 160 और 512 बिट्स के बीच किसी भी SHA एल्गोरिदम के उत्पादन को संग्रहीत नहीं कर सकता है। कोई समान प्रकार नहीं है जो पोस्टग्रेज के मानक वितरण में फिट बैठता है। आप एक बना सकते हैं ... असफल होना, byteaजैसे कि डिफ़ॉल्ट - जैसे pg_crypto करता है।
इरविन ब्रांडीसेट्टर

2

मैं एक में MD5 संग्रहीत करेंगे textया varcharस्तंभ। विभिन्न चरित्र डेटा प्रकारों के बीच कोई प्रदर्शन अंतर नहीं है। आप md5 मानों की लंबाई को कम करने के varchar(xxx)लिए यह सुनिश्चित करना चाहते हैं कि md5 मान कभी निश्चित लंबाई से अधिक न हो।

बड़ी सूची आमतौर पर वास्तव में तेज़ नहीं होती हैं, ऐसा कुछ करना बेहतर होता है:

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

एक और विकल्प जिसे कभी-कभी तेज कहा जाता है वह है सरणी का उपयोग करना:

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

जैसा कि आप सिर्फ समानता के लिए तुलना कर रहे हैं, एक नियमित बीट्री इंडेक्स ठीक होना चाहिए। दोनों प्रश्नों को ऐसे सूचकांक का उपयोग करने में सक्षम होना चाहिए (विशेषकर यदि आप पंक्तियों के केवल एक छोटे से हिस्से का चयन कर रहे हैं।


बिट (128) या हेक्स (32) का उपयोग न करने का कोई विशेष कारण? मानों को इस तरह के क्षेत्र में बड़े करीने से फिट होने की गारंटी दी जाती है, और मैं सौंपे जा रहे बुरे मूल्यों से रक्षा करना चाहता हूं।
बोबोकॉपी

3
@bobocopy: पोस्टग्रेज में कोई "हेक्स" डेटा प्रकार नहीं है। मैंने कभी भी इस bitप्रकार का उपयोग नहीं किया है इसलिए मैं उस पर टिप्पणी नहीं कर सकता। पंक्तियों की आपकी अपेक्षित संख्या को देखते हुए, इरविन के सुझाव से बेहतर लगता है कि अंतरिक्ष की बचत के कारण आप इसे UUID के रूप में प्राप्त कर सकते हैं
a_horse_with_no_name

-1

एक अन्य विकल्प 4 INTEGER या 2 BIGINT कॉलम का उपयोग करना है।


2
भंडारण आकार के संदर्भ में, या तो विकल्प फिट होगा, लेकिन यह कितना सुविधाजनक होगा? शायद आप एक उदाहरण दिखाने के लिए अपने उत्तर का विस्तार कर सकते हैं या अन्यथा समझा सकते हैं।
एंड्री एम
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.