JSONB: अपना डेटाबेस बर्बाद करने का सबसे अच्छा तरीका
JSONB शक्तिशाली है, उपयोगी है, और जब आप एक ब्लॉब को अपना असली स्कीमा बनने दें तो इसे गलत इस्तेमाल करना बहुत आसान है।
PostgreSQL ने JSONB इसलिए जोड़ा ताकि आप बिना पहले से कठोर स्कीमा परिभाषित किए अर्ध-संरचित डेटा स्टोर कर सकें। विचार सही था: कभी-कभी आपको वास्तव में पता नहीं होता कि डेटा कैसा दिखेगा, या यह पारंपरिक कॉलम के समझदार होने से भी अधिक तेज़ी से बदलता है।
इसलिए मायने रखता है क्योंकि JSONB कोई गलती नहीं है। बहुत सी प्रणालियों में यह समस्या स्थान का सबसे साफ़ प्रतिनिधित्व है। यदि आप थर्ड-पार्टी वेबहुक पेलोड, वर्ज़न्ड इवेंट बॉडी, फ़ीचर फ़्लैग, या LLM कॉन्फ़िगरेशन ऑब्जेक्ट स्टोर कर रहे हैं जहाँ हर प्रदाता और मॉडल थोड़ा अलग और लगातार बदलता हुआ विकल्प सेट उजागर करता है, तो सब कुछ फर्स्ट-क्लास कॉलम में ज़बरदस्ती करना मददगार से ज़्यादा अजीब हो सकता है।
समस्या यह है कि JSONB स्कीमा निर्णयों को स्थगित करने का सबसे आसान तरीका भी है — बिना यह स्वीकार किए कि आप उन्हें स्थगित कर रहे हैं। इरादे और कार्यान्वयन के बीच कहीं, यह डेटाबेस का “मैं बाद में अपना कमरा साफ़ कर लूँगा” वाला हिस्सा बन गया। वह अस्थायी समाधान जिसके लिए आपने छह महीने पहले हाथ बढ़ाया था? वह अभी भी वहीं है, और अब प्रोडक्शन उस पर निर्भर करता है।
मैं लगातार एक ही पैटर्न देखता हूँ। एक टीम JSONB कॉलम इसलिए जोड़ती है क्योंकि उन्हें आवश्यकताओं के बारे में निश्चित नहीं है। वे खुद से वादा करते हैं कि चीज़ें स्थिर होने के बाद वे नॉर्मलाइज़ कर लेंगे। तीन साल बाद, उस कॉलम में वह यूज़र प्रोफ़ाइल होने वाली चीज़ के चालीस अलग-अलग वर्ज़न होते हैं, जिसे पंद्रह सेवाएँ क्वेरी कर रही हैं जो हर एक यह मानती हैं कि अंदर क्या है।
तकनीकी ऋण JSONB स्वयं नहीं है। यह उस अंतराल है जो आपने खुद से कहा कि आप क्या बना रहे हैं और आपने वास्तव में क्या बनाया: एक अनदस्तावेज़ीकृत स्कीमा-ऑन-रीड प्रणाली।
आमतौर पर क्या होता है
आप एक फ़ीचर जोड़ रहे हैं और आपको निश्चित नहीं है कि उपयोगकर्ताओं को twitter_handle चाहिए या bluesky_handle या कुछ और। स्कीमा के बारे में सोचने के बजाय, आप यह करते हैं:
CREATE TABLE users ( id SERIAL PRIMARY KEY, profile JSONB);यह काम करता है। आप फ़ीचर शिप करते हैं, अगले वाले पर बढ़ते हैं, फिर अगले वाले पर। JSONB कॉलम पृष्ठभूमि में चुपचाप बढ़ता जाता है।
यह रास्ते का मोड़ है। यदि profile एक अपारदर्शी ब्लॉब बना रहता है जिसे user.id द्वारा फ़ेच किया जाता है, तो आप शायद ठीक हैं। यदि यह वह प्राथमिक स्थान बनने लगता है जहाँ बिज़नेस डेटा रहता है, तो ट्रेडऑफ़ तेज़ी से बदल जाते हैं।
प्रोडक्ट पूछता है: “न्यू यॉर्क में कितने उपयोगकर्ता हैं?”
आप लिखते हैं:
SELECT count(*) FROM users WHERE profile->>'location' = 'New York';Postgres एक पूर्ण टेबल स्कैन करता है। हर एक पंक्ति।
तो आप GIN इंडेक्स जोड़ते हैं। शायद वह अभी भी स्वीकार्य है। कभी-कभी ऐसा ही होता है। लेकिन अब आप वास्तविक जटिलता और स्टोरेज लागत चुका रहे हैं क्योंकि एक फ़ील्ड जो फर्स्ट-क्लास रिलेशनल डेटा की तरह व्यवहार करती है, कभी फर्स्ट-क्लास कॉलम बनी ही नहीं।
वर्ष 1: स्कीमा ड्रिफ्ट
आपके पास एक ही कॉलम में डेटा के तीन वर्ज़न हैं।
- पंक्ति 1:
{"city": "NYC"} - पंक्ति 1000:
{"location": "NYC"} - पंक्ति 5000:
{"address": {"city": "New York"}}
आपका एप्लिकेशन कोड अब ऐसा दिखता है:
const city = user.location || user.city || user.address?.city || "Unknown";आपने स्कीमा को हटाया नहीं। आपने बस सत्यापन और स्थिरता जाँच को डेटाबेस से बिखरे हुए एप्लिकेशन कोड में स्थानांतरित कर दिया।
JSONB का वास्तव में उपयोग कब करें
JSONB के वैध उपयोग मामले हैं। कई बार यह बिल्कुल ठीक है, और कभी-कभी यह सबसे अच्छा विकल्प उपलब्ध होता है।
महत्वपूर्ण अंतर “संरचित अच्छा, JSON बुरा” नहीं है। यह इसके करीब है:
- क्या डेटा मुख्य रूप से एक स्थिर प्राथमिक कुंजी द्वारा समग्र रूप से फ़ेच किया जाता है?
- क्या कुंजियाँ प्रदाताओं, वर्ज़न, टेनेंट या समय के बीच महत्वपूर्ण रूप से भिन्न होती हैं?
- क्या आप कुछ ज्ञात फ़ील्ड को क्वेरी कर रहे हैं, या हर स्प्रिंट नए पथ क्वेरी बना रहे हैं?
- क्या एप्लिकेशन वर्ज़निंग और सत्यापन को जानबूझकर स्वयं संभालता है, या बस अंदाज़े पर काम चल रहा है?
वैध JSONB उपयोग मामले
-
वेबहुक पेलोड: आपको Stripe, Slack, या GitHub से डेटा प्राप्त होता है। आपके पास स्कीमा पर शून्य नियंत्रण है। आप शायद इसे कभी क्वेरी नहीं करेंगे। आपको बस डीबगिंग या रिप्ले के लिए इसे स्टोर करने की ज़रूरत है। JSONB के लिए बिल्कुल सही।
-
लॉगिंग और इवेंट स्ट्रीम: एप्लिकेशन लॉग, ऑडिट ट्रेल, त्रुटि संदर्भ। ये लिखने-भारी हैं, शायद ही कभी विशिष्ट फ़ील्ड द्वारा क्वेरी किए जाते हैं, और अक्सर थोक में विश्लेषण या एनालिटिक्स प्लेटफ़ॉर्म पर निर्यात किए जाते हैं। यहाँ JSONB ठीक है।
-
उपयोगकर्ता प्राथमिकताएँ और सेटिंग्स: सेटिंग्स ऑब्जेक्ट जहाँ आपके पास 100+ बूलियन फ़्लैग हैं, ज़्यादातर ग़लत हैं, और आप हमेशा पूरे ब्लॉब को उपयोगकर्ता ID द्वारा फ़ेच कर रहे हैं। आप
WHERE preferences->>'theme' = 'dark'नहीं चला रहे हैं। JSONB काम करता है। -
LLM प्रदाता / मॉडल कॉन्फ़िग: यह सबसे स्पष्ट आधुनिक उदाहरणों में से एक है। OpenAI, Anthropic, Gemini, ओपन-वेट लोकल मॉडल, और विक्रेता-विशिष्ट गेटवे सभी ओवरलैपिंग लेकिन अलग पैरामीटर उजागर करते हैं। एक ही प्रदाता के भीतर भी, मॉडल क्षमताएँ और विकल्प नाम विकसित होते हैं। एक JSONB कॉन्फ़िग ब्लॉब अक्सर इससे कहीं अधिक ईमानदार है कि यह pretending करे कि
temperature,top_p,reasoning_effort,json_schema,tool_choice, और बीस अन्य नॉब सभी सार्वभौमिक कॉलम होने चाहिए। JSONB यहाँ अक्सर सही एब्स्ट्रैक्शन है। -
API रिस्पॉन्स कैशिंग: आप पूरे API रिस्पॉन्स को कैश कर रहे हैं। डेटाबेस बस एक तेज़ Redis है। आप कैश कुंजी द्वारा फ़ेच करते हैं, कभी नेस्टेड प्रॉपर्टी द्वारा नहीं। JSONB उपयुक्त है।
-
इवेंट सोर्सिंग: आप अपरिवर्तनीय इवेंट पेलोड स्टोर कर रहे हैं। आपकी क्वेरी हमेशा “मुझे एग्रीगेट X के लिए सभी इवेंट दें” समय के क्रम में होती है। आप इवेंट प्रॉपर्टी पर कभी
WHEREक्लॉज़ नहीं चलाते। JSONB फ़िट बैठता है। -
विस्तारणीयता सतह: इंटीग्रेशन, प्लगइन सेटिंग्स, प्रति-टेनेंट ओवरराइड, मार्केटप्लेस मेटाडेटा, प्रदाता क्षमताएँ, या “अतिरिक्त” फ़ील्ड जहाँ आप स्पष्ट रूप से आकार को सबटाइप के अनुसार भिन्न होने की उम्मीद करते हैं। JSONB सही अनुबंध हो सकता है, समझौता नहीं।
अंगूठे का नियम: यदि एप्लिकेशन दस्तावेज़ को ज्ञात कुंजी द्वारा फ़ेच करता है और इसे सत्यापित/वर्ज़न करना जानता है, तो JSONB उत्कृष्ट हो सकता है। यदि बिज़नेस नेस्टेड कुंजियों के बारे में लगातार रिलेशनल प्रश्न पूछ रहा है, तो वे फ़ील्ड कॉलम बनने की कोशिश कर रहे हैं।
सबसे अच्छा पैटर्न अक्सर हाइब्रिड होता है
बहुत सी परिपक्व प्रणालियाँ यहाँ पहुँचती हैं:
CREATE TABLE llm_requests ( id UUID PRIMARY KEY, provider TEXT NOT NULL, model TEXT NOT NULL, status TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), config JSONB NOT NULL);यह आमतौर पर दोनों चरम सीमाओं से बेहतर होता है।
provider,model,status, औरcreated_atफर्स्ट-क्लास कॉलम हैं क्योंकि आप उन्हें फ़िल्टर, जॉइन, एग्रीगेट और इंडेक्स करेंगे।configJSONB बना रहता है क्योंकि सटीक विकल्प सतह मॉडल-विशिष्ट, प्रदाता-विशिष्ट है, और विकसित होने की संभावना है।
यह “नॉर्मलाइज़ करने में विफल” नहीं है। यह रेखा को सही स्थान पर खींचना है।
बड़े पैमाने पर: नॉर्मलाइज़ेशन से ऑब्जेक्ट वर्ज़निंग बेहतर
यहाँ यह दिलचस्प हो जाता है। पर्याप्त बड़े पैमाने पर, “सही” समाधान नॉर्मलाइज़ेशन नहीं है — यह ऑब्जेक्ट वर्ज़निंग है।
यदि आपके पास अरबों पंक्तियाँ हैं और बार-बार स्कीमा विकास होता है, तो कॉलम माइग्रेट करना महँगा होता है। Stripe, GitHub, और Netflix जैसी कंपनियाँ सब कुछ नॉर्मलाइज़ नहीं करतीं। इसके बजाय:
CREATE TABLE entities ( id UUID PRIMARY KEY, version INT NOT NULL, data JSONB NOT NULL);आपका एप्लिकेशन जानता है कि version: 1, version: 2, version: 3 को कैसे पढ़ना है। नए फ़ील्ड के लिए कोई डेटाबेस माइग्रेशन नहीं। कोड बैकवर्ड कंपैटिबिलिटी संभालता है।
यह एक आर्किटेक्चरल निर्णय है, आलस्य नहीं। यह डेटाबेस जटिलता का एप्लिकेशन जटिलता से व्यापार करता है। कभी-कभी यह बिल्कुल सही व्यापार होता है, खासकर जब दस्तावेज़ स्वाभाविक रूप से वर्ज़न्ड होता है और ऐप कैनोनिकल इंटरप्रेटर होता है।
विफलता मोड “JSONB का उपयोग करना” नहीं है। विफलता मोड बिना वर्ज़निंग, सत्यापन, प्रमोशन नियमों, या दस्तावेज़ डेटा और रिलेशनल डेटा के बीच स्पष्ट सीमा के JSONB का उपयोग करना है।
वे प्रश्न जो वास्तव में मायने रखते हैं
JSONB कॉलम जोड़ने से पहले पूछें:
- क्या हम नेस्टेड फ़ील्ड को
WHERE,JOIN,GROUP BY, याORDER BYमें नियमित रूप से क्वेरी करेंगे? - क्या हम इस स्कीमा को नियंत्रित करते हैं, या यह बाहरी रूप से परिभाषित और अस्थिर है?
- क्या आकार जानबूझकर रिकॉर्डों के बीच विषम है?
- क्या हमारे पास एप्लिकेशन-स्तरीय सत्यापन और वर्ज़निंग है?
- कौन से फ़ील्ड बाद में परिचालनात्मक आयाम बनने की संभावना रखते हैं?
यदि #1 का उत्तर “हाँ, लगातार” है, तो यह कॉलम के लिए एक मज़बूत संकेत है।
यदि #2 और #3 के उत्तर “हाँ” हैं, तो JSONB शायद आपके लिए वास्तविक काम कर रहा है।
जाल से बाहर निकलना
यदि आप पहले से इस गड्ढे में हैं, तो खुदाई बंद करें।
- ऑडिट:
jsonb_object_keysचलाएँ और वास्तविक आकार ड्रिफ्ट का निरीक्षण करें, न कि वह आकार जो आप मानते हैं कि मौजूद है। - प्रमोट: उन फ़ील्ड की पहचान करें जिन्हें आप सबसे अधिक फ़िल्टर, जॉइन, सॉर्ट या रिपोर्ट करते हैं। उन्हें असली कॉलम बनाएँ।
- सत्यापित: JSONB में जो कुछ भी बचा है उसके लिए एप्लिकेशन या डेटाबेस-स्तरीय सत्यापन जोड़ें।
- वर्ज़न: यदि ब्लॉब वास्तविक डोमेन डेटा है, तो इसे स्पष्ट रूप से वर्ज़न करें।
- ट्रिम: प्रमोट किए गए कॉलम स्थापित होने के बाद ब्लॉब से डुप्लिकेट कुंजियाँ हटाएँ।
खुद से मत कहो कि हर ब्लॉब को नॉर्मलाइज़ होना चाहिए। यह भी मत कहो कि स्थायी बिज़नेस सिमेंटिक्स वाला ब्लॉब “अस्थायी” है।
JSONB तब शानदार है जब दस्तावेज़ वास्तव में दस्तावेज़-आकार का होता है। यह खतरनाक है जब यह नकली मूँछ पहने रिलेशनल स्कीमा होता है।
संसाधन
- PostgreSQL JSONB दस्तावेज़ीकरण
- JSONB इंडेक्सिंग रणनीतियाँ
- JSONB बनाम रिलेशनल कॉलम कब उपयोग करें
- PostgreSQL स्कीमा डिज़ाइन सर्वोत्तम अभ्यास