PostgreSQL में UUID बनाम bigint: स्केल करने वाले ID का चयन
PostgreSQL में UUID बनाम bigint: इंडेक्स आकार, सॉर्ट ऑर्डर, शार्डिंग-तैयारी की तुलना और यह IDs APIs, वेब और मोबाइल ऐप्स में कैसे बहते हैं।

क्यों ID का चुनाव जितना दिखता है उससे ज़्यादा मायने रखता है
हर पंक्ति (row) को PostgreSQL तालिका में दोबारा ढूँढने का एक स्थिर तरीका चाहिए। यही ID करता है: यह रिकॉर्ड की विशिष्ट पहचान है, आमतौर पर यह प्राथमिक कुंजी बनती है, और यह रिश्तों के लिए गोंठ की तरह काम करती है। दूसरी तालिकाएँ इसे विदेशी कुंजी के रूप में रखेंगी, क्वेरीज इस पर JOIN करेंगी, और ऐप इसे “उस ग्राहक”, “उस चालान” या “उस सपोर्ट टिकट” के हैंडल के रूप में पास करेंगे।
क्योंकि IDs हर जगह पहुँच जाते हैं, इसलिए चुनाव सिर्फ डेटाबेस का मामला नहीं रहता। यह बाद में इंडेक्स आकार, लिखने के पैटर्न, क्वेरी की स्पीड, कैश हिट दरों और यहां तक कि प्रोडक्ट कार्यों जैसे एनालिटिक्स, इम्पोर्ट और डिबगिंग में दिखता है। यह यह भी प्रभावित करता है कि आप URLs और APIs में क्या प्रदर्शित करते हैं, और किसी मोबाइल ऐप के लिए डेटा को सुरक्षित तरीके से स्टोर और सिंक करना कितना आसान होगा।
ज्यादातर टीमें PostgreSQL में UUID बनाम bigint की तुलना करती हैं। सरल शर्तों में, आप चुन रहे हैं:
- bigint: एक 64-बिट संख्या, अक्सर एक sequence से जनरेट की जाती है (1, 2, 3...).
- UUID: एक 128-बिट पहचान, अक्सर यादृच्छिक दिखती है, या समय-आधारित तरीके से जनरेट की जाती है।
कोई भी ऑप्शन हर मामले में जीतता नहीं है। Bigint कॉम्पैक्ट और इंडेक्स तथा सॉर्टिंग के लिए अनुकूल रहता है। UUIDs तब मजबूत विकल्प होते हैं जब आपको सिस्टम के पार वैश्विक अद्वितीयता चाहिए, सार्वजनिक IDs को सुरक्षित रखना हो, या आप अनेक स्थानों (कई सेवाएँ, ऑफलाइन मोबाइल, या भविष्य में शार्डिंग) पर डेटा बनवाने की उम्मीद करते हों।
एक उपयोगी नियम: निर्णय इस बात पर लें कि आपका डेटा कैसे बनकर और साझा होगा, न कि सिर्फ आज कैसे स्टोर होता है।
bigint और UUID का सरल परिचय
जब लोग PostgreSQL में UUID बनाम bigint की तुलना करते हैं, तो वे दो तरीकों के बीच चुन रहे होते हैं कि पंक्तियों को कैसे नाम दें: एक छोटा काउंटर जैसा नंबर, या एक लंबा वैश्विक रूप से अद्वितीय मान।
एक bigint ID 64-बिट पूर्णांक है। PostgreSQL में आप आम तौर पर इसे एक identity column (या पुराने serial पैटर्न) से जनरेट करते हैं। डेटाबेस अंदर एक सीक़ेंस रखता है और हर बार इंसर्ट पर अगला नंबर देता है। इसका मतलब है कि IDs आमतौर पर 1, 2, 3, 4... होते हैं। यह सरल, पढ़ने में आसान और टूल्स व रिपोर्ट्स में मित्रवत है।
एक UUID (Universally Unique Identifier) 128 बिट है। आप इसे अक्सर 36 अक्षरों के रूप में हाइफ़न के साथ देखेंगे, जैसे 550e8400-e29b-41d4-a716-446655440000। सामान्य प्रकारों में शामिल हैं:
- v4: यादृच्छिक UUIDs। कहीं भी जनरेट करना आसान है, पर ये बनने के क्रम में क्रमबद्ध नहीं होते।
- v7: समय-क्रमित UUIDs। फिर भी अद्वितीय होते हैं, पर डिज़ाइन में ये समय के साथ मोटे तौर पर बढ़ते हैं।
स्टोरेज पहला व्यावहारिक अंतर है: bigint 8 बाइट उपयोग करता है, जबकि UUID 16 बाइट उपयोग करता है। यह आकार का अंतर इंडेक्स में दिखता है और कैश हिट दरों को प्रभावित कर सकता है (डेटाबेस मेमोरी में कम एंट्री फिट होती है)।
यह भी सोचें कि IDs डेटाबेस के बाहर कहां दिखेंगे। Bigint IDs URLs में छोटे होते हैं और लॉग्स या सपोर्ट टिकट से पढ़ना आसान होता है। UUIDs लंबे होते हैं और टाइप करने में झंझट हैं, पर अनुमान लगाना कठिन होते हैं और क्लाइंट्स पर सुरक्षित रूप से जनरेट किए जा सकते हैं।
इंडेक्स आकार और टेबल बिलो: क्या बदलता है
सबसे बड़ा व्यावहारिक अंतर आकार है। Bigint 8 बाइट है; UUID 16 बाइट। यह मामूली लग सकता है जब तक आपको याद न हो कि इंडेक्स आपके IDs को कई बार दोहराते हैं।
आपकी प्राथमिक कुंजी इंडेक्स तेजी से मेमोरी में गर्म (hot) रहनी चाहिए ताकि प्रदर्शन अच्छा रहे। छोटी इंडेक्स का मतलब अधिक हिस्सा shared buffers और CPU कैश में फिट होना है, जिससे लुकअप और JOIN के लिए कम डिस्क पढ़ना पड़ेगा। UUID प्राथमिक कुंजियों के साथ, समान पंक्ति संख्या के लिए इंडेक्स आम तौर पर नोटिस करने लायक बड़ा होता है।
गैर-प्राथमिक (secondary) इंडेक्स इसका गुणक हैं। PostgreSQL B-tree इंडेक्स में, हर सेकेंडरी इंडेक्स एंट्री में प्राथमिक कुंजी का मान भी स्टोर होता है (ताकि डेटाबेस रो को ढूँढ सके)। इसलिए चौड़े IDs सिर्फ प्राथमिक कुंजी इंडेक्स को नहीं, बल्कि आप द्वारा जोड़े गए हर दूसरे इंडेक्स को भी बढ़ाते हैं। यदि आपके पास तीन सेकेंडरी इंडेक्स हैं, तो UUIDs से आया अतिरिक्त 8 बाइट प्रभावी रूप से चार स्थानों पर दिखेगा।
फॉरेन कीज़ और जोइन तालिकाएँ भी इसका अनुभव करती हैं। कोई भी तालिका जो आपके ID का संदर्भ देती है वह उस मान को अपनी पंक्तियों और इंडेक्सों में स्टोर करती है। एक many-to-many जोइन टेबल अक्सर मुख्यतः दो विदेशी कुंजियाँ और थोड़ा ओवरहेड होता है, इसलिए कुंजी चौड़ाई डबल होने से उसका फ़ुटप्रिंट काफी बदल सकता है।
प्रैक्टिस में:
- UUIDs आम तौर पर प्राथमिक और सेकेंडरी इंडेक्स को बड़ा कर देते हैं, और अंतर कई इंडेक्स के साथ संयुक्त हो जाता है।
- बड़े इंडेक्स का मतलब अधिक मेमोरी दबाव और लोड के तहत अधिक पेज पढ़ाई।
- जितनी अधिक तालिकाएँ कुंजी का संदर्भ देंगी (इवेंट्स, लॉग्स, जोइन टेबल्स), आकार का अंतर उतना ही महत्वपूर्ण होगा।
यदि एक यूज़र ID users, orders, order_items और audit_log में दिखाई देता है, तो वही मान उन सब तालिकाओं में स्टोर और इंडेक्स किया जाता है। इसलिए चौड़ी ID चुनना सिर्फ स्टोरेज का ही निर्णय नहीं, बल्कि एक व्यापक डिज़ाइन निर्णय है।
सॉर्ट ऑर्डर और लिखने के पैटर्न: क्रमिक बनाम रैंडम IDs
अधिकांश PostgreSQL प्राथमिक कुंजियाँ B-tree इंडेक्स पर रहती हैं। B-tree सबसे अच्छा तब काम करता है जब नई पंक्तियाँ इंडेक्स के अंत के पास आती हैं, क्योंकि डेटाबेस कम reshuffling के साथ जोड़ सकता है।
क्रमिक IDs: स्टोरेज के लिए अनुकूल और अनुमाननीय
bigint identity या sequence के साथ, नए IDs समय के साथ बढ़ते हैं। इंसर्ट्स आम तौर पर इंडेक्स के दाहिने हिस्से पर लगते हैं, इसलिए पेज भरे रहते हैं, कैश गर्म रहता है, और PostgreSQL को कम अतिरिक्त काम करना पड़ता है।
यह भी मायने रखता है भले ही आप कभी ORDER BY id न चलाएँ। लिखने के रास्ते को अभी भी इंडेक्स में नए कुंजी को सही.sorted स्थान पर रखना पड़ता है।
रैंडम UUIDs: अधिक प्रसार, अधिक चर्न
एक रैंडम UUID (सामान्यतः UUIDv4) इंसर्ट्स को पूरे इंडेक्स में फैला देता है। इससे पेज स्प्लिट्स की संभावना बढ़ती है, जहाँ PostgreSQL नया इंडेक्स पेज आवंटित करता है और एंट्रीज़ को स्थानांतरित करता है ताकि जगह बनाई जा सके। परिणाम होता है अधिक लिखावट का विस्तार: अधिक इंडेक्स बाइट्स लिखे जाते हैं, अधिक WAL उत्पन्न होता है, और अक्सर बाद में अधिक बैकग्राउंड काम (vacuum और bloat प्रबंधन) होता है।
समय-आधारित UUIDs कहानी बदल देते हैं। UUIDs जो ज्यादातर समय के साथ बढ़ते हैं (जैसे UUIDv7-शैली या अन्य समय-आधारित स्कीम) स्थानीयता का अधिकांश हिस्सा बहाल कर देते हैं, जबकि वे अभी भी 16 बाइट रहते हैं और API में UUID जैसी ही दिखती हैं।
आप यह अंतर तब सबसे ज़्यादा महसूस करेंगे जब आपके पास उच्च इंसर्ट दरें हों, बड़ी तालिकाएँ हों जो मेमोरी में फिट न हों, और कई सेकेंडरी इंडेक्स मौजूद हों। यदि आप पेज-स्प्लिट्स से होने वाले लिखने के लेटेंसी स्पाइक्स के प्रति संवेदनशील हैं, तो हॉट राइट तालिकाओं पर पूरी तरह रैंडम IDs से बचें।
उदाहरण: मोबाइल ऐप लॉग्स लेने वाली व्यस्त इवेंट्स टेबल आम तौर पर क्रमिक कुंजियों या समय-आधारित UUIDs के साथ बेहतर चलेगी बजाय पूर्ण रैंडम UUIDs के।
वास्तविक प्रदर्शन प्रभाव जिसे आप महसूस कर सकते हैं
अधिकांश वास्तविक दुनिया की धीमीपन “UUIDs धीरे हैं” या “bigints तेज़ हैं” जैसा सरल नहीं है। यह इस बात पर निर्भर करता है कि डेटाबेस को आपकी क्वेरी का जवाब देने के लिए क्या छूना पड़ता है।
क्वेरी प्लान मुख्य रूप से इस पर ध्यान देते हैं कि क्या वे फ़िल्टर के लिए इंडेक्स स्कैन उपयोग कर सकते हैं, कुंजी पर तेज़ जॉइन कर सकते हैं, और क्या तालिका भौतिक रूप से व्यवस्थित है (या पर्याप्त करीब) ताकि रेंज रीड्स सस्ते हों। bigint प्राथमिक कुंजी के साथ, नई पंक्तियाँ मोटे तौर पर बढ़ते क्रम में आती हैं, इसलिए प्राथमिक कुंजी इंडेक्स कॉम्पैक्ट और स्थानीयता-अनुकूल रहता है। रैंडम UUIDs के साथ, इंसर्ट्स इंडेक्स में बिखर जाते हैं, जिससे अधिक पेज स्प्लिट्स और ऑन-डिस्क क्रम का अव्यवस्थित होना हो सकता है।
रीड्स वह जगह हैं जहाँ कई टीमें पहले फर्क नोटिस करती हैं। बड़े कीज़ का मतलब बड़े इंडेक्स और बड़े इंडेक्स का मतलब कि कम उपयोगी पेज रैम में फिट होते हैं। इससे कैश हिट दर घटती है और IO बढ़ता है, विशेषकर उन स्क्रीनों पर जो JOIN-भारी होती हैं जैसे “ग्राहक जानकारी के साथ ऑर्डर्स सूची”। यदि आपका वर्किंग सेट मेमोरी में नहीं फिट होता, तो UUID-प्रधान स्कीमा आपको उस सीमा पर पहले ले जा सकता है।
लिखे भी बदल सकते हैं। रैंडम UUID इंसर्ट्स इंडेक्स में चर्न बढ़ा सकते हैं, जो autovacuum पर दबाव बढ़ाता है और व्यस्त अवधि के दौरान लेटेंसी स्पाइक्स के रूप में दिख सकता है।
यदि आप PostgreSQL में UUID बनाम bigint का बेंचमार्क करते हैं, तो ईमानदार रहें: समान स्कीमा, समान इंडेक्स, समान fillfactor, और पर्याप्त पंक्तियाँ ताकि RAM से अधिक हो (10k नहीं)। p95 लेटेंसी और IO मापें, और गर्म और ठंडी कैश दोनों की जाँच करें।
यदि आप AppMaster पर PostgreSQL में ऐप बनाते हैं, तो यह अक्सर सूची पृष्ठों की धीमी रहने और भारी डेटाबेस लोड के रूप में पहले दिखाई देता है बजाय कि यह कोई स्पष्ट “CPU समस्या” हो।
सार्वजनिक-सामने वाले सिस्टम में सुरक्षा और प्रयोज्यता
यदि आपके IDs डेटाबेस छोड़ते हैं और URLs, API प्रतिक्रियाओं, सपोर्ट टिकटों और मोबाइल स्क्रीन में दिखते हैं, तो चुनाव सुरक्षा और दैनिक उपयोगिता दोनों को प्रभावित करता है।
Bigint IDs इंसानों के लिए आसान हैं। वे छोटे होते हैं, आप उन्हें फ़ोन पर पढ़ सकते हैं, और आपका सपोर्ट टीम जल्दी पैटर्न जैसे “सभी फेलिंग ऑर्डर लगभग 9,200,000 के आसपास हैं” देख सकती है। यह डिबगिंग को तेज़ कर सकता है, खासकर जब आप लॉग्स या कस्टमर स्क्रीनशॉट से काम कर रहे हों।
UUIDs तब मददगार होते हैं जब आप पहचानपत्रों को सार्वजनिक रूप से दिखाते हैं। UUID अनुमान लगाना कठिन बनाते हैं, इसलिए साधारण स्क्रैपिंग जैसे /users/1, /users/2, /users/3 काम नहीं करते। यह बाहरी लोगों के लिए यह अनुमान लगाना भी कठिन बनाता है कि आपके पास कितनी रिकॉर्ड्स हैं।
जाल यह सोचना है कि “अंदाज़ न लगाया जा सके” का अर्थ “सुरक्षित” है। यदि प्राधिकरण चेक कमजोर हैं, तो अनुमाननीय bigint IDs का दुरुपयोग जल्दी किया जा सकता है, पर UUIDs भी साझा लिंक, लीक हुए लॉग, या कैश्ड API प्रतिक्रिया से चोरी हो सकते हैं। सुरक्षा को ID छिपाने पर मत छोड़ें—यह अनुमतियों से आनी चाहिए।
एक व्यावहारिक तरीका:
- हर पढ़ाई और लिखाई पर ओनरशिप या रोल चेक लागू करें।
- यदि आप IDs सार्वजनिक APIs में दिखाते हैं, तो UUIDs या अलग सार्वजनिक टोकन का उपयोग करें।
- यदि आप मानव-अनुकूल संदर्भ चाहते हैं, तो अंदरूनी bigint रखें।
- ID में संवेदनशील अर्थ (जैसे यूज़र टाइप) एन्कोड न करें।
उदाहरण: एक कस्टमर पोर्टल चालान IDs दिखाता है। अगर चालान bigint उपयोग कर रहे हैं और आपकी API केवल यह जाँचती है कि “चालान मौजूद है”, तो कोई व्यक्ति संख्याएँ इटरेट कर के दूसरों के चालान डाउनलोड कर सकता है। पहले जाँच ठीक करें। फिर देखें कि सार्वजनिक चालान IDs के लिए UUIDs जोखिम और सपोर्ट लोड कम करते हैं या नहीं।
AppMaster जैसे प्लेटफार्मों में, जहाँ IDs जनरेट किए गए APIs और मोबाइल ऐप्स के माध्यम से बहते हैं, सबसे सुरक्षित डिफ़ॉल्ट है सुसंगत प्राधिकरण + एक ऐसा ID फॉर्मैट जो आपके क्लाइंट्स आसानी से संभाल सकें।
APIs और मोबाइल ऐप्स में IDs कैसे बहते हैं
आप जो डेटाबेस प्रकार चुनते हैं वह सिर्फ डेटाबेस तक नहीं रहता—यह हर सीमा तक रिसता है: URLs, JSON पेलोड, क्लाइंट स्टोरेज, लॉग्स और एनालिटिक्स तक।
यदि आप बाद में ID प्रकार बदलते हैं, तो ब्रेकेज़ शायद कभी "सिर्फ़ एक माइग्रेशन" नहीं होते। विदेशी कुंजियाँ हर जगह बदलनी पड़ती हैं, न केवल मुख्य तालिका में। ORMs और कोड जनरेटर मॉडल फिर से जनरेट कर सकते हैं, पर इंटीग्रेशन पुरानी फ़ॉर्मैट की उम्मीद कर रहे होते हैं। यहाँ तक कि एक साधारण GET /users/123 एंडपॉइंट भी पचाना मुश्किल हो जाता है जब ID 36-अक्षर UUID में बदल जाए। आपको कैशेस, मैसेज कतारें और IDs जहाँ स्टोर किए गए थे उन सबको अपडेट करना पड़ता है।
APIs के लिए सबसे बड़ा चुनाव है फ़ॉर्मैट और वैलिडेशन। Bigints संख्याओं के रूप में चलते हैं, पर कुछ सिस्टम (और कुछ भाषाएँ) बहुत बड़े मानों को फ्लोटिंग पॉइंट के रूप में पार्स कर के सटीकता खो बैठते हैं। UUIDs स्ट्रिंग्स के रूप में चलते हैं, जो पार्सिंग के लिए सुरक्षित है, पर आपको सख्त वैलिडेशन चाहिये ताकि “लगभग UUID” जंक लॉग्स और डेटाबेस में न पहुँच जाए।
मोबाइल पर, IDs लगातार सीरियलाइज़ और स्टोर होते हैं: JSON प्रतिक्रियाएँ, लोकल SQLite तालिकाएँ, और ऑफलाइन कतारें जो कार्रवाई को तब तक सेव करती हैं जब तक नेटवर्क वापस न आए। संख्यात्मक IDs छोटे होते हैं, पर स्ट्रिंग UUIDs अक्सर उन्हें अस्पष्ट टोकन की तरह ट्रीट करना आसान बनाते हैं। असली दर्द तब आता है जब असंगति हो: एक लेयर इसे integer के रूप में स्टोर करती है, दूसरी टेक्स्ट के रूप में, और तुलना या जोइन नाज़ुक हो जाते हैं।
कहीं तक पहुँचने वाले कुछ नियम जो टीमों को परेशानी से बचाते हैं:
- APIs के लिए एक canonical प्रतिनिधित्व चुनें (अक्सर string) और उस पर टिके रहें।
- एज पर IDs को वैलिडेट करें और स्पष्ट 400 त्रुटियाँ लौटाएँ।
- लोकल कैशेस और ऑफलाइन कतारों में वही प्रतिनिधित्व स्टोर करें।
- सेवाओं के बीच IDs को हमेशा एक ही फ़ील्ड नाम और फ़ॉर्मैट में लॉग करें।
यदि आप AppMaster जैसे जनरेटेड स्टैक से वेब और मोबाइल क्लाइंट बना रहे हैं, तो एक स्थिर ID कॉन्ट्रैक्ट और भी ज़्यादा मायने रखता है क्योंकि यह हर जनरेटेड मॉडल और अनुरोध का हिस्सा बन जाता है।
शार्डिंग-रेडीनेस और वितरित सिस्टम
“शार्डिंग-रेडी” का मतलब ज्यादातर यह है कि आप कई जगहों पर ID बना सकें बिना यूनिकनेस टूटे, और बाद में डेटा को नोड्स के बीच मूव करने पर हर विदेशी कुंजी को फिर से लिखने की ज़रूरत न पड़े।
UUIDs बहु-रीजन या मल्टी-राइटर सेटअप में लोकप्रिय हैं क्योंकि कोई भी नोड अनुरोध किए बिना अद्वितीय ID बना सकता है। इससे समन्वय कम होता है और विभिन्न क्षेत्रों में लिखने को स्वीकार करना और बाद में डेटा मर्ज करना आसान हो जाता है।
Bigint अभी भी काम कर सकता है, पर आपको योजना चाहिए। सामान्य विकल्पों में संख्यात्मक रेंज्स प्रति शार्ड अलोकेट करना (शार्ड 1 उपयोग करता है 1–1B, शार्ड 2 उपयोग करता है 1B–2B), शार्ड प्रिफिक्स के साथ अलग सीक्वेन्स चलाना, या Snowflake-जैसे IDs (समय-बेस्ड बिट्स प्लस मशीन/शार्ड बिट्स) शामिल हैं। ये UUIDs से छोटे इंडेक्स रखने और कुछ ordering बनाए रखने में मदद कर सकते हैं, पर वे संचालनात्मक नियम जोड़ते हैं जिन्हें आपको लागू रखना होगा।
दैनंदिन मायने रखने वाले ट्रेड-ऑफ:
- समन्वय: UUID करीब-करीब नहीं के बराबर मांगते; bigint अक्सर रेंज प्लानिंग या जनरेटर सेवा मांगता है।
- टकराव (collisions): UUID टकराव अत्यंत कम होते हैं; bigint तब सुरक्षित है जब अलोकेशन नियम कभी ओवरलैप न हों।
- ऑर्डरिंग: कई bigint योजनाएँ मोटे तौर पर समय-आधारित होती हैं; UUID अक्सर प्रभावी रूप से रैंडम होता है जब तक आप टाइम-ऑर्डर्ड वेरिएंट न चुनें।
- जटिलता: शार्डेड bigint तब तक सरल रहता है जब तक टीम अनुशासित रहे।
कई टीमों के लिए “शार्डिंग-रेडी” असल में “माइग्रेशन-रेडी” का मतलब रखता है। यदि आप आज किसी एक डेटाबेस पर हैं, तो वही ID चुनें जो वर्तमान काम को आसान बनाए। यदि आप पहले से ही कई लेखक बना रहे हैं (उदाहरण के लिए, AppMaster पर जनरेटेड APIs और मोबाइल ऐप्स के जरिए), तो पहले ही तय कर लें कि IDs कैसे बनाए और मान्य किए जाएंगे।
स्टेप-बाय-स्टेप: सही ID रणनीति चुनना
शुरूआत अपने ऐप के असली आकार को नाम देकर करें। एक एकल PostgreSQL डेटाबेस एक क्षेत्र में अलग ज़रूरतें रखता है बनाम एक मल्टी-टेनेंट सिस्टम, एक सेटअप जो बाद में क्षेत्र द्वारा विभाजित हो सकता है, या एक ऑफलाइन-प्रथम मोबाइल ऐप जिसे ऑफलाइन रिकॉर्ड बनाकर बाद में सिंक करना होगा।
अगला, ईमानदार रहें कि IDs कहाँ दिखाई देंगे। यदि पहचान पत्र केवल आपके बैकेंड के अंदर रहते हैं (जॉब्स, अंदरूनी टूल, एडमिन पैनल), तो सरलता अक्सर जीतती है। यदि IDs URLs, लॉग्स जो ग्राहकों के साथ साझा किए जाते हैं, सपोर्ट टिकट, या मोबाइल डीप लिंक में दिखाई देते हैं, तो अनुमाननीयता और गोपनीयता ज्यादा मायने रखती है।
ऑर्डरिंग को एक बाद की बात के रूप में न लें—इसे निर्णय में शामिल करें। यदि आप "नवीनतम पहले" फ़ीड, स्थिर पेजिनेशन, या स्कैन करने में आसान ऑडिट ट्रेल्स पर निर्भर करते हैं, तो क्रमिक IDs (या टाइम-ऑर्डर्ड IDs) आश्चर्य कम कर देते हैं। यदि ऑर्डरिंग प्राथमिक कुंजी से जुड़ी नहीं है, तो आप PK चुनाव को अलग रखकर टाइमस्टैम्प द्वारा सॉर्ट कर सकते हैं।
एक व्यावहारिक निर्णय प्रवाह:
- अपनी आर्किटेक्चर वर्गीकृत करें (सिंगल DB, मल्टी-टेनेंट, मल्टी-रीजन, ऑफलाइन-प्रथम) और तय करें कि क्या आप संभवतः कई स्रोतों से डेटा मर्ज कर सकते हैं।
- तय करें कि IDs सार्वजनिक पहचान हैं या केवल आंतरिक।
- अपने पेजिनेशन और ऑर्डरिंग की जरूरतें कन्फ़र्म करें। यदि आपको इनसर्शन ऑर्डर चाहिए तो पूरी तरह रैंडम IDs से बचें।
- यदि आप UUID चुनते हैं, तो उद्देश्य के साथ संस्करण चुनें: अनपेक्षितता के लिए random (v4), या बेहतर इंडेक्स स्थानीयता के लिए time-ordered।
- प्रारंभ में नियम लॉक कर दें: एक canonical टेक्स्ट फॉर्म, केस नियम, वैलिडेशन, और हर API में IDs कैसे लौटेंगे और स्वीकार किए जाएँ।
उदाहरण: यदि एक मोबाइल ऐप "ड्राफ्ट ऑर्डर्स" ऑफलाइन बनाता है, UUIDs डिवाइस को सर्वर को दिखाने से पहले सुरक्षित रूप से IDs जनरेट करने देते हैं। AppMaster जैसे टूल्स में यह सुविधाजनक होता है क्योंकि वही ID फॉर्मैट डेटाबेस से लेकर API तक और वेब एवं नेटिव ऐप्स तक बिना विशेष केसिंग के बह सकता है।
सामान्य गलतियाँ और जाल जिनसे बचें
अधिकांश ID बहसें इस वजह से गलत हो जाती हैं कि लोग किसी एक वजह के चलते ID प्रकार चुन लेते हैं और बाद में साइड-इफेक्ट्स से आश्चर्यचकित होते हैं।
एक आम गलती है हॉट-राइट तालिका पर पूरी तरह रैंडम UUIDs का उपयोग करना और फिर यह आश्चर्य करना कि इंसर्ट्स क्यों स्पाइकी लगते हैं। रैंडम मान नए पंक्तियों को इंडेक्स में फैला देते हैं, जिससे पेज स्प्लिट्स और डेटाबेस पर अधिक काम होता है। यदि तालिका लेखाई-भारी है, तो इंसर्ट लोकैलिटी के बारे में सोचें।
एक और बार-बार होने वाली समस्या सेवाओं और क्लाइंट्स के बीच ID प्रकार को मिलाना है। उदाहरण के लिए, एक सेवा bigint उपयोग करती है, दूसरी UUID, और आपकी API में दोनों संख्यात्मक और स्ट्रिंग ID आ जाते हैं। यह अक्सर सूक्ष्म बग्स में बदल जाता है: JSON पार्सर बड़े नंबरों पर सटीकता खो देते हैं, मोबाइल कोड किसी स्क्रीन पर ID को नंबर मानता है और दूसरी स्क्रीन पर स्ट्रिंग, या कैशिंग कीज़ मेल न खाएँ।
तीसरा जाल है “अंदाज़ न लगाया जा सके” को सुरक्षा मान लेना। भले ही आप UUIDs का उपयोग करें, आपको अभी भी उचित अनुमति चेक्स चाहिए।
अंत में, टीमें ID प्रकार को देर में बदल देती हैं बिना योजना के। सबसे मुश्किल भाग सिर्फ प्राथमिक कुंजी नहीं होती—यह हर चीज़ है जो उससे जुड़ी होती है: विदेशी कुंजियाँ, जोइन टेबल्स, URLs, एनालिटिक्स इवेंट्स, मोबाइल डीप लिंक और स्टोर किया गया क्लाइंट स्टेट।
दर्द से बचने के लिए:
- सार्वजनिक APIs के लिए एक ID प्रकार चुनें और उस पर टिके रहें।
- क्लाइंट्स में IDs कोopaque स्टリング्स की तरह व्यवहार करें ताकि संख्यात्मक किनारे के मामले न आएँ।
- कभी भी ID यादृच्छिकता को एक्सेस कंट्रोल के रूप में उपयोग न करें।
- यदि माइग्रेट करना है तो API का संस्करण बनाएं और दीर्घकालिक क्लाइंट्स के लिए योजना बनाएं।
AppMaster जैसे कोड-जनरेटिंग प्लेटफॉर्म के साथ यदि आप बनाते हैं तो संगति और भी ज़्यादा मायने रखती है क्योंकि वही ID प्रकार डेटाबेस स्कीमा से जनरेटेड बैकएंड और वेब/मोबाइल ऐप्स तक बहता है।
निर्णय से पहले का शीघ्र चेकलिस्ट
यदि आप अटक गए हैं, सिद्धांत से शुरू मत करें। यह सोच कर शुरू करें कि आपका प्रोडक्ट एक साल में कैसा दिखेगा, और वह ID कितनी जगहों पर जाएगा।
पूछें:
- सबसे बड़ी तालिकाएँ 12–24 महीनों में कितनी बड़ी होंगी, और क्या आप वर्षों का इतिहास रखेंगے?
- क्या आपको IDs चाहिए जो मोटे तौर पर निर्माण समय के अनुसार सॉर्ट हों ताकि पेजिंग और डिबगिंग आसान रहे?
- क्या एक से अधिक सिस्टम एक ही समय में रिकॉर्ड बनाएंगे, जिसमें ऑफलाइन मोबाइल ऐप्स या बैकग्राउंड जॉब्स भी शामिल हों?
- क्या ID URLs, सपोर्ट टिकट, एक्सपोर्ट या ग्राहकों के साथ साझा किए गए स्क्रीनशॉट में दिखेगा?
- क्या हर क्लाइंट ID को उसी तरीके से संभाल सकता है (वेब, iOS, Android, स्क्रिप्ट्स), वैलिडेशन और स्टोरेज सहित?
इनका उत्तर देने के बाद, प्लंबिंग की युक्ति करें। यदि आप bigint उपयोग करते हैं, तो सुनिश्चित करें कि हर वातावरण (खासकर लोकल डेवलप और इम्पोर्ट्स) में ID जनरेशन की स्पष्ट योजना हो। यदि आप UUID उपयोग करते हैं, तो सुनिश्चित करें कि आपकी API कॉन्ट्रैक्ट और क्लाइंट मॉडल स्ट्रिंग IDs को सुसंगत रूप से संभालते हैं, और आपकी टीम उन्हें पढ़ने और तुलना करने में सहज है।
एक त्वरित वास्तविकता परीक्षण: यदि मोबाइल ऐप को ऑफलाइन रहते हुए ऑर्डर बनाना है और बाद में सिंक करना है, तो UUIDs अक्सर समन्वय कम कर देते हैं। यदि आपका ऐप ज्यादातर ऑनलाइन है और आप सरल, कॉम्पैक्ट इंडेक्स चाहते हैं, तो bigint आम तौर पर आसान होता है।
यदि आप AppMaster में बना रहे हैं, तो जल्दी निर्णय लें ताकि आपका डेटाबेस मॉडल, API एंडपॉइंट और मोबाइल क्लाइंट तब तक सुसंगत रहें जब आप प्रोजेक्ट को फिर से जनरेट कर रहे हों और बढ़ा रहे हों।
एक वास्तविक उदाहरण परिदृश्य
एक छोटी कंपनी के पास एक आंतरिक ऑपरेशंस टूल, एक कस्टमर पोर्टल और फील्ड स्टाफ के लिए एक मोबाइल ऐप है। तीनों एक ही PostgreSQL डेटाबेस को एक API के जरिए हिट करते हैं। दिन भर नई रिकॉर्ड्स बनती हैं: टिकट, फ़ोटोज़, स्टेटस अपडेट्स और चालान।
bigint IDs के साथ, API पेलोड कॉम्पैक्ट और पढ़ने में आसान होते हैं:
{ "ticket_id": 4821931, "customer_id": 91244 }
पेजिनेशन स्वाभाविक लगता है: ?after_id=4821931&limit=50. id से सॉर्ट करना आम तौर पर निर्माण समय से मेल खाता है, इसलिए “नवीनतम टिकट” तेज़ और अनुमान्य होता है। डिबगिंग भी सरल है: सपोर्ट “ticket 4821931” मांग सकता है और अधिकतर लोग इसे बिना गलती के टाइप कर सकते हैं।
UUIDs के साथ, पेलोड लंबा हो जाता है:
{ "ticket_id": "3f9b3c0a-7b9c-4bf0-9f9b-2a1b3c5d1d2e" }
यदि आप random UUID v4 का उपयोग करते हैं, इंसर्ट्स इंडेक्स में चारों ओर गिरते हैं। इसका मतलब अधिक इंडेक्स चर्न और थोड़ा कठिन दैनंदिन डिबगिंग (कॉपी/पेस्ट सामान्य बन जाता है)। पेजिनेशन अक्सर “after id” की बजाय कर्सर-शैली टोकन पर शिफ्ट हो जाती है।
यदि आप टाइम-ऑर्डर्ड UUIDs का उपयोग करते हैं, तो आप “नवीनतम पहले” व्यवहार का अधिकांश हिस्सा बनाए रखते हैं जबकि सार्वजनिक URLs में अनुमान-रहित IDs भी मिलती हैं।
प्रैक्टिस में टीमें आम तौर पर चार चीजें नोटिस करती हैं:
- कितनी बार IDs इंसान द्वारा टाइप किए जाते हैं बनाम कॉपी किए जाते हैं
- क्या “id से सॉर्ट” करना “created से सॉर्ट” से मेल खाता है
- कर्सर पेजिनेशन कितना साफ़ और स्थिर लगता है
- एक रिकॉर्ड को लॉग्स, API कॉल्स और मोबाइल स्क्रीन के पार ट्रेस करना कितना आसान है
अगले कदम: एक डिफ़ॉल्ट चुनें, टेस्ट करें, और मानकीकृत करें
अधिकांश टीमें इस पर अटक जाती हैं क्योंकि वे परफेक्ट उत्तर चाहते हैं। आपको परफेक्ट की ज़रूरत नहीं—एक ऐसा डिफ़ॉल्ट चाहिए जो आज आपके प्रोडक्ट पर फिट हो, साथ में एक तेज़ तरीका जिससे आप साबित कर सकें कि यह बाद में आपको हानि नहीं पहुंचाएगा।
आप मानकीकृत कर सकते हैं:
- जब आप सबसे छोटे इंडेक्स, पूर्वानुमान्य ऑर्डरिंग और आसान डिबगिंग चाहते हैं तो bigint उपयोग करें।
- जब IDs सार्वजनिक URLs में अनुमान-रहित होने चाहिए, आप ऑफलाइन क्रिएशन की उम्मीद करते हों, या सिस्टमों के बीच टकराव कम करना हो तो UUID उपयोग करें।
- यदि आप भविष्य में टेनेंट या रीजन द्वारा डेटा विभाजित करने की संभावना रखते हैं, तो ऐसी ID योजना चुनें जो नोड्स के पार काम करे (UUID, या समन्वित bigint योजना)।
- एक चीज को डिफ़ॉल्ट बनाएं और अपवाद दुर्लभ रखें। संगति अक्सर किसी तालिका का माइक्रो-ऑप्टिमाइज़ेशन करने से बेहतर होती है।
तय करने से पहले एक छोटा स्पाइक चलाएँ। एक वास्तविक पंक्ति आकार के साथ एक तालिका बनाएं, 1–5 मिलियन पंक्तियाँ डालें, और तुलना करें (1) इंडेक्स आकार, (2) इंसर्ट समय, और (3) कुछ सामान्य क्वेरीज़ प्राथमिक कुंजी और कुछ सेकेंडरी इंडेक्स के साथ। इसे अपने वास्तविक हार्डवेयर और वास्तविक डेटा के साथ करें।
यदि आपको लगता है कि बाद में बदलना पड़ सकता है, तो माइग्रेशन को बोरिंग बनाने की योजना बनाएं:
- नया ID कॉलम और एक यूनिक इंडेक्स जोड़ें।
- डुअल-राइट: नई पंक्तियों के लिए दोनों IDs भरें।
- बैच में पुराने पंक्तियों को बैकफिल करें।
- APIs और क्लाइंट्स को नया ID स्वीकार करने के लिए अपडेट करें (ट्रांज़िशन के दौरान पुराना भी काम करता रहे)।
- पढ़ाई पर कटओवर करें, फिर पुराने की को हटाएँ जब लॉग्स और मेट्रिक्स साफ़ दिखें।
यदि आप AppMaster (appmaster.io) पर बना रहे हैं, तो जल्दी निर्णय लेना फायदेमंद होता है क्योंकि ID कन्वेंशन आपके PostgreSQL मॉडल, जनरेटेड APIs, और वेब व नेटिव मोबाइल ऐप्स में बहता है। प्रकार महत्वपूर्ण है, पर एक बार वास्तविक उपयोगकर्ताओं और कई क्लाइंट्स के साथ, संगति अधिक मायने रखती है।
सामान्य प्रश्न
डिफ़ॉल्ट रूप से bigint चुनें जब आपके पास एकल PostgreSQL डेटाबेस हो, अधिकतर लिखाई सर्वर पर ही हो, और आप कॉम्पैक्ट इंडेक्स और पूर्वानुमान योग्य इंसर्ट व्यवहार चाहते हों। UUIDs चुनें जब IDs को कई जगहें पर जनरेट करना हो (कई सेवाएँ, ऑफलाइन मोबाइल, भविष्य में शार्डिंग) या जब आप सार्वजनिक IDs को अनुमान लगने से रोकना चाहें।
क्योंकि ID कई जगह कॉपी हो जाता है: प्राथमिक कुंजी इंडेक्स में, हर सेकेंडरी इंडेक्स में (रो प्वाइंटर के रूप में), अन्य तालिकाओं में विदेशी की कॉलम्स में, और जोइन टेबल्स में। UUID 16 बाइट है जबकि bigint 8 बाइट; इसलिए यह आकार का अंतर आपके स्कीमा में कई बार बढ़ता है और कैश हिट दरों को घटा सकता है।
हॉट इंसर्ट तालिकाओं पर, हाँ। रैंडम UUIDs (जैसे v4) B-tree के पूरे दायरे में इंसर्ट फैलाते हैं, जिससे पेज स्प्लिट्स और इंडेक्स चर्न बढ़ता है। अगर आप UUID चाहते हैं पर चिकने लिखाई भी चाहते हैं, तो टाइम-ऑर्डर्ड UUID रणनीति का उपयोग करें ताकि नए कीज़ ज्यादातर अंत में आएँ।
अक्सर यह IO के रूप में दिखता है, न कि CPU के रूप में। बड़े कीज़ का मतलब बड़े इंडेक्स हैं, और बड़े इंडेक्स का मतलब कि कम पेज मेमोरी में फिट होते हैं—इसलिए जॉइन और लुकअप्स अधिक पढ़ाई करवा सकते हैं। यह अंतर बड़े तालिकाओं, जॉइन-भारी क्वेरीज़ और उन सिस्टमों में जो RAM में पूरा वर्किंग सेट नहीं रखते, ज्यादा दिखाई देता है।
UUIDs आसान अनुमान से बचाते हैं जैसे /users/1, पर वे प्राधिकरण की जगह नहीं लेते। अगर आपकी अनुमति-जाँच कमजोर है, तो UUIDs भी लीक हो सकते हैं और दुबारा उपयोग हो सकते हैं। UUIDs को सार्वजनिक पहचान के रूप में सुविधाजनक मानें, असली सुरक्षा सख्त एक्सेस कंट्रोल से आएगी।
एक केनोनिकल प्रतिनिधित्व चुनें और उसी पर टिके रहें। एक व्यवहार्य डिफ़ॉल्ट यह है कि API में IDs को स्ट्रिंग्स के रूप में ट्रीट करें (भले ही DB bigint हो), क्योंकि यह क्लाइंट-साइड संख्यात्मक एज परेशन की परेशानियों से बचाता है और वैलिडेशन सरल रखता है। जो भी चुनें, वेब, मोबाइल, लॉग्स और कैश में संगतता बनाए रखें।
यदि बड़ी संख्याएँ फ्लोटिंग-पॉइंट के रूप में पार्स हुईं, तो कुछ क्लाइंट्स (विशेषकर JavaScript) में bigint टूट सकता है। UUIDs इसे टालते हैं क्योंकि वे स्ट्रिंग्स हैं, पर वे लंबे होते हैं और यदि ठीक से वैरिफाई न करें तो गड़बड़ी कर सकते हैं। सबसे सुरक्षित तरीका है संगति—सभी जगह एक ही प्रकार और API एज पर स्पष्ट वैलिडेशन।
UUIDs आसान विकल्प हैं क्योंकि किसी भी नोड पर बिना केंद्रीय अनुक्रम के अद्वितीय ID बन सकते हैं। bigint अभी भी काम कर सकता है, पर आपको रेंज अलोकेशन, शार्ड-प्रिफिक्स या Snowflake-शैली जनरेटर जैसी योजनाएँ अपनानी होंगी और उन नियमों को हमेशा लागू रखना होगा। यदि आप सबसे सरल वितरित कहानी चाहते हैं तो UUIDs चुनें (सुझाए गए रूप से टाइम-ऑर्डर्ड)।
प्राथमिक कुंजी प्रकार बदलना केवल एक कॉलम से बहुत अधिक को छूता है। आपको विदेशी कुंजियाँ, जोइन टेबल्स, API कॉन्ट्रैक्ट, क्लाइंट स्टोरेज, कैश्ड डेटा, एनालिटिक्स इवेंट्स और किसी भी इंटीग्रेशन को अपडेट करना होगा जो IDs को नंबर या स्ट्रिंग के रूप में स्टोर करते हैं। अगर बदलाव संभव है, तो धीरे-धीरे माइग्रेशन की योजना बनाएं: नया ID कॉलम जोड़ें, डुअल-राइट, ब्याच में बैकफिल, API अपडेट, और फिर कटओवर।
डेटाबेस दक्षता के लिए अंदरूनी bigint कुंजी रखें, और URLs तथा बाहरी APIs के लिए एक अलग सार्वजनिक UUID (या टोकन) जोड़ें। यह आपको कॉम्पैक्ट इंडेक्स और आंतरिक डिबगिंग में मानवीय-फ्रेंडली संदर्भ देता है जबकि सार्वजनिक पहचान आसानी से अनुमान न की जा सके। ज़रूरी बात यह है कि शुरू में तय कर लें कौन सा “पब्लिक ID” होगा और इसे मिलाकर न रखें।


