डेटा मॉडल
मैं छह-अंकीय स्केल पर एक डायरेक्टरी साइट के लिए स्कीमा कैसे डिज़ाइन करता हूँ। एंटिटीज़, एट्रिब्यूट्स, स्लग्स, इंडेक्सेस, और डिज़ाइन विकल्प जो संचयी होते हैं।
स्कीमा निर्णय जो संचयी होते हैं
28K पंक्तियों (HostList.io) या 137K पंक्तियों (Not Another Sunday) पर एक डायरेक्टरी साइट पहले हफ्ते के दौरान किए गए स्कीमा निर्णयों पर सफल या विफल होती है। सबसे महत्वपूर्ण निर्णय: एंटिटीज़ को कैसे नॉर्मलाइज़ किया जाता है, एट्रिब्यूट्स को कैसे स्टोर किया जाता है, स्लग्स कैसे जेनरेट किए जाते हैं, क्या इंडेक्स किया जाता है।
इनमें से हर एक छोटे स्केल पर रिवर्सिबल है और छह-अंकीय स्केल पर ठीक करने के लिए निषेधात्मक रूप से महंगा है। उन्हें जल्दी सही करें।
एंटिटीज़, एट्रिब्यूट्स, जंक्शन्स
एक एंटिटीज़ टेबल जिसमें कंपनी, स्थान, या उत्पाद के लिए एक मास्टर पंक्ति हो। स्थिर UUID प्राइमरी कुंजी, प्रत्येक आंतरिक एट्रिब्यूट के लिए एक कॉलम (नाम, स्थापना_वर्ष, प्राथमिक_बाजार)। इस लेयर पर एट्रिब्यूट्स को JSON कॉलम में जामने से बचें; वे प्रथम-श्रेणी कॉलम के रूप में क्वेरी योग्य होने चाहिए।
प्रत्येक ऑर्थोगोनल अक्ष के लिए एक attributes टेबल। categories (प्रत्येक पंक्ति एक category है), regions (प्रत्येक पंक्ति एक region है), price_tiers (प्रत्येक पंक्ति एक tier है)। ये वे dimensions हैं जिनके आर-पार entities को group किया जाता है।
Junction tables के माध्यम से many-to-many relationships: entity_categories, entity_regions, entity_features। entities table में कभी भी comma-separated category lists store न करें; junction tables ही वह चीज़ हैं जो filtering, listing, और counting को तेज़ बनाते हैं।
Slugs हमेशा के लिए।
एक बार जब directory page को Google index कर ले, तो slug को नहीं बदलना चाहिए। Entity name से deterministic slug generation करें, collision handling के लिए numeric suffix के साथ: nginx-hosting, nginx-hosting-2 अगर collision हो।
किसी भी historical slug को current canonical slug से map करने वाली एक अलग slug_history table maintain करें। जब entity का नाम बदलना हो (correction, rebrand), नया canonical slug generate करें, पुराना slug history table में जाए, और permanent 301 redirect automatically set up हो।
28K-row site पर slug stability को गलत तरीके से handle करने की cost catastrophic है: आप जितनी redirect chain बनाते हैं वह ranking erosion है, और बाद में कोई अच्छा fix नहीं है।
Row level पर indexability।
Entities table में एक indexable boolean column जोड़ें, default true है, ऐसी logic के साथ कि यह false हो जाता है जब entity quality thresholds को पूरा नहीं करता (insufficient unique content, missing required attributes, no incoming internal links)।
Sitemap query indexable = true पर filter करती है। Page renderer indexable = false होने पर noindex meta add करता है। दोनों same column से derive होते हैं, इसलिए atomically sync में रहते हैं।
HostList.io पर, 28K में से लगभग 18K entities किसी भी समय indexable हैं। बाकी 10K crawlable हैं लेकिन indexable नहीं हैं, जो indexed surface को clean रखते हैं और बाकी indexed pages को stronger बनाते हैं।
जो इंडेक्स मायने रखते हैं
पेज-रेंडर लुकअप के लिए slug पर इंडेक्स। Sitemap क्वेरी के लिए (indexable, last_updated_at) पर इंडेक्स। कैटेगरी-लिस्टिंग पेज के लिए जंक्शन टेबल पर (category_id, indexable) पर इंडेक्स। रीजनल पेज के लिए (region_id, indexable) पर इंडेक्स।
छह अंकों की पंक्तियों के स्केल पर, एक बिना इंडेक्स वाली पेज-रेंडर क्वेरी सेकंड लेती है। सही इंडेक्स के साथ, यह मिलीसेकंड लेती है। बिल्ड-टाइम प्रभाव बढ़ता जाता है: 28K पेज रेंडर प्रत्येक 100ms पर 47 मिनट लगते हैं; प्रत्येक 10ms पर 5 मिनट लगते हैं।
स्केल पर हर पेज-रेंडर क्वेरी पर EXPLAIN ANALYZE चलाएं। डिफॉल्ट क्वेरी प्लानर डायरेक्टरी-शेप्ड जॉइन पर आपकी उम्मीद से ज़्यादा बार गलत निर्णय लेता है।