प्रसंग
मैं एक डेटाबेस डिजाइन कर रहा हूं (PostgreSQL 9.6 पर) जो एक वितरित एप्लिकेशन से डेटा संग्रहीत करेगा। एप्लिकेशन की वितरित प्रकृति के कारण, मैं SERIAL
संभावित दौड़-स्थितियों के कारण अपनी प्राथमिक कुंजी के रूप में ऑटो-इंक्रीमेंट पूर्णांक ( ) का उपयोग नहीं कर सकता ।
प्राकृतिक समाधान एक UUID, या विश्व स्तर पर अद्वितीय पहचानकर्ता का उपयोग करना है। पोस्टग्रेज एक बिल्ट-इन UUID
प्रकार के साथ आता है , जो एक आदर्श फिट है।
UUID के साथ मुझे जो समस्या है वह डीबगिंग से संबंधित है: यह एक गैर-मानव-अनुकूल स्ट्रिंग है। पहचानकर्ता ff53e96d-5fd7-4450-bc99-111b91875ec5
मुझे कुछ भी नहीं बताता है, जबकि ACC-f8kJd9xKCd
, अद्वितीय होने की गारंटी नहीं है, मुझे बताता है कि मैं एक ACC
वस्तु के साथ काम कर रहा हूं ।
एक प्रोग्रामिंग परिप्रेक्ष्य से, कई अलग-अलग वस्तुओं से संबंधित एप्लिकेशन क्वेरी को डीबग करना आम है। मान लीजिए कि प्रोग्रामर गलत तरीके ACC
से ORD
(ऑर्डर) टेबल पर (खाता) ऑब्जेक्ट खोजता है । मानव-पढ़ने योग्य पहचानकर्ता के साथ, प्रोग्रामर तुरंत समस्या की पहचान करता है, जबकि यूयूआईडी का उपयोग करते हुए वह कुछ समय बिताएगा जो गलत था।
मुझे UUID की "गारंटीकृत" विशिष्टता की आवश्यकता नहीं है; मैं है संघर्ष के बिना कुंजी पैदा करने के लिए कुछ कमरे की जरूरत है, लेकिन UUID overkill है। इसके अलावा, सबसे खराब स्थिति, यह दुनिया का अंत नहीं होगा अगर टक्कर हुई (डेटाबेस इसे अस्वीकार कर देता है और एप्लिकेशन पुनर्प्राप्त कर सकता है)। इसलिए, व्यापार-नापसंद माना जाता है, एक छोटी लेकिन मानव-अनुकूल पहचानकर्ता मेरे उपयोग के मामले के लिए आदर्श समाधान होगा।
एप्लिकेशन ऑब्जेक्ट्स की पहचान करना
मेरे साथ आया पहचानकर्ता निम्न प्रारूप में है: {domain}-{string}
जहां {domain}
ऑब्जेक्ट डोमेन (खाता, आदेश, उत्पाद) के साथ बदल दिया गया है और {string}
एक बेतरतीब ढंग से उत्पन्न स्ट्रिंग है। कुछ मामलों में, {sub-domain}
यादृच्छिक स्ट्रिंग से पहले डालने का भी अर्थ हो सकता है । आइए विशिष्टता की गारंटी के उद्देश्य के लिए {domain}
और इसकी लंबाई को अनदेखा करें {string}
।
यदि अनुक्रमण / क्वेरी प्रदर्शन में मदद करता है तो प्रारूप का एक निश्चित आकार हो सकता है।
समस्या
यह जानते हुए:
- मैं एक प्रारूप के साथ प्राथमिक कुंजी रखना चाहता हूं
ACC-f8kJd9xKCd
। - ये प्राथमिक कुंजियाँ कई तालिकाओं का हिस्सा होंगी।
- इन सभी कुंजियों का उपयोग 6NF डेटाबेस पर कई जॉइन / रिलेशनशिप पर किया जाएगा।
- अधिकांश तालिकाओं में मध्यम-से-बड़े आकार (~ 1M पंक्तियों के औसत) ~ 100M पंक्तियों के साथ सबसे बड़े वाले) होंगे।
प्रदर्शन के संबंध में, इस कुंजी को संग्रहीत करने का सबसे अच्छा तरीका क्या है?
नीचे चार संभव समाधान दिए गए हैं, लेकिन चूंकि मुझे डेटाबेस के साथ बहुत कम अनुभव है, इसलिए मैं अनिश्चित हूं (यदि कोई हो) सबसे अच्छा है।
माना समाधान
1. स्ट्रिंग के रूप में स्टोर ( VARCHAR
)
(पोस्टग्रैज के बीच कोई अंतर नहीं है CHAR(n)
और VARCHAR(n)
इसलिए मैं अनदेखा कर रहा हूं CHAR
)।
कुछ शोध के बाद, मुझे पता चला है कि स्ट्रिंग की तुलना VARCHAR
, विशेष रूप से जुड़ने के संचालन पर, उपयोग करने की तुलना में धीमी है INTEGER
। यह समझ में आता है, लेकिन क्या यह कुछ है जो मुझे इस पैमाने पर चिंता करनी चाहिए?
2. बाइनरी के रूप में स्टोर करें ( bytea
)
Postgres के विपरीत, MySQL में एक देशी UUID
प्रकार नहीं है । BINARY
36-बाइट के बजाय 16-बाइट फ़ील्ड का उपयोग करके UUID को संग्रहीत करने का तरीका बताने वाले कई पोस्ट हैं VARCHAR
। इन पोस्टों ने मुझे कुंजी को बाइनरी ( bytea
पोस्टग्रेज पर) के रूप में संग्रहीत करने का विचार दिया ।
यह आकार बचाता है, लेकिन मैं प्रदर्शन से अधिक चिंतित हूं। मेरे पास बहुत कम भाग्य था जो एक स्पष्टीकरण खोज रहा था जिस पर तुलना तेज है: द्विआधारी या स्ट्रिंग वाले। मेरा मानना है कि बाइनरी तुलना तेजी से होती है। यदि वे हैं, तो bytea
शायद इससे बेहतर है VARCHAR
, भले ही प्रोग्रामर को अब हर बार डेटा को एनकोड / डिकोड करना पड़े ।
मैं गलत हो सकता है, लेकिन मैं दोनों लगता है bytea
और VARCHAR
बाइट द्वारा (या चरित्र चरित्र द्वारा) की तुलना करेंगे (समानता) बाइट। क्या इस चरण-दर-चरण तुलना को "स्किप" करने का एक तरीका है और बस "पूरी बात" की तुलना करें? (मुझे ऐसा नहीं लगता, लेकिन यह जाँच की लागत नहीं है)।
मुझे लगता है कि भंडारण bytea
करना सबसे अच्छा उपाय है, लेकिन मुझे आश्चर्य है कि क्या कोई अन्य विकल्प है जिसे मैं अनदेखा कर रहा हूं। इसके अलावा, एक ही चिंता जो मैंने समाधान 1 पर व्यक्त की है, वह सच है: तुलनाओं पर ओवरहेड पर्याप्त है जिसके बारे में मुझे चिंता करनी चाहिए?
"सृजनात्मक समाधान
मैं दो बहुत ही "रचनात्मक" समाधानों के साथ आया, जो काम कर सकते हैं, मैं सिर्फ इस हद तक अनिश्चित हूं (यानी अगर मुझे उन्हें एक तालिका में कुछ हज़ार से अधिक पंक्तियों को स्केल करने में परेशानी होगी)।
3. स्टोर करें UUID
लेकिन इसके साथ एक "लेबल" संलग्न करें
यूयूआईडी का उपयोग नहीं करने का मुख्य कारण यह है कि प्रोग्रामर आवेदन को बेहतर तरीके से डिबग कर सकें। लेकिन क्या होगा अगर हम दोनों का उपयोग कर सकते हैं: डेटाबेस सभी कुंजियों को UUID
केवल s के रूप में संग्रहीत करता है , लेकिन यह क्वेरी के पहले / बाद में ऑब्जेक्ट को लपेटता है।
उदाहरण के लिए, प्रोग्रामर पूछता है ACC-{UUID}
, डेटाबेस ACC-
भाग की उपेक्षा करता है , परिणाम प्राप्त करता है, और उन सभी को वापस लौटाता है {domain}-{UUID}
।
हो सकता है कि यह कुछ हैकरी के साथ संग्रहीत प्रक्रियाओं या कार्यों के साथ संभव होगा, लेकिन कुछ सवाल दिमाग में आते हैं:
- क्या यह (प्रत्येक क्वेरी पर डोमेन को हटाना / जोड़ना) एक पर्याप्त ओवरहेड है?
- क्या यह भी संभव है?
मैंने पहले कभी संग्रहीत प्रक्रियाओं या कार्यों का उपयोग नहीं किया है, इसलिए मुझे यकीन नहीं है कि क्या यह संभव है। क्या कोई प्रकाश को बहा सकता है? अगर मैं प्रोग्रामर और संग्रहीत डेटा के बीच एक पारदर्शी परत जोड़ सकता हूं, तो यह एक सटीक समाधान लगता है।
4. IPv6 के रूप में (मेरा पसंदीदा) स्टोर cidr
हाँ, आप इसे पढ़ें। यह पता चला है कि IPv6 पता प्रारूप मेरी समस्या को पूरी तरह हल करता है ।
- मैं पहले कुछ ऑक्टेट्स में डोमेन और उप-डोमेन जोड़ सकता हूं, और शेष लोगों को यादृच्छिक स्ट्रिंग के रूप में उपयोग कर सकता हूं।
- टक्कर बाधाओं ठीक हैं। (हालांकि मैं 2 ^ 128 का उपयोग नहीं करूंगा, लेकिन यह अभी भी ठीक है।)
- समानता तुलना (उम्मीद है) अनुकूलित है, इसलिए मुझे बस उपयोग करने से बेहतर प्रदर्शन मिल सकता है
bytea
। - मैं वास्तव में कुछ दिलचस्प तुलना कर सकता हूं, जैसे
contains
, डोमेन और उनके पदानुक्रम का प्रतिनिधित्व कैसे किया जाता है, इस पर निर्भर करता है।
उदाहरण के लिए, मान लें कि मैं 0000
डोमेन "उत्पादों" का प्रतिनिधित्व करने के लिए कोड का उपयोग करता हूं । कुंजी 0000:0db8:85a3:0000:0000:8a2e:0370:7334
उत्पाद का प्रतिनिधित्व करेगी 0db8:85a3:0000:0000:8a2e:0370:7334
।
यहां मुख्य प्रश्न यह है: क्या डेटा प्रकार bytea
का उपयोग cidr
करने पर कोई मुख्य लाभ या नुकसान है ?
varchar
कई अन्य समस्याओं के बीच होगा । मुझे pg के डोमेन के बारे में नहीं पता था, जिसके बारे में सीखना बहुत अच्छा है। मुझे लगता है कि यदि किसी दिए गए क्वेरी का उपयोग सही ऑब्जेक्ट का उपयोग करने के लिए किया जा रहा है तो मान्य करने के लिए डोमेन का उपयोग किया जाता है, लेकिन यह अभी भी एक गैर-पूर्णांक सूचकांक होने पर निर्भर करेगा। यकीन नहीं होता कि serial
यहां (एक लॉक स्टेप के बिना) उपयोग करने का एक "सुरक्षित" तरीका है ।
varchar
। इसे एक FK
integer
प्रकार बनाने पर विचार करें और इसके लिए एक लुकअप तालिका जोड़ें। इस तरह से आपके पास मानव पठनीयता दोनों हो सकती है और आप अपने समग्र PK
को सम्मिलित / अद्यतन विसंगतियों (गैर-मौजूद डोमेन डाल) से बचाएंगे।
text
अधिक बेहतर है varchar
। पर देखो depesz.com/2010/03/02/charx-vs-varcharx-vs-varchar-vs-text और postgresql.org/docs/current/static/datatype-character.html
ACC-f8kJd9xKCd
। “PR यह अच्छे पुराने मिश्रित प्राथमिक कुंजी के लिए एक नौकरी प्रतीत होता है ।