प्रश्न और दायरा
एक प्रतिस्पर्धी गेम के लिए एक सीज़नल लीडरबोर्ड डिज़ाइन करें। एक सीज़न में 50 मिलियन सक्रिय खिलाड़ी होते हैं, प्रति सेकंड 200,000 तक स्कोर अपडेट प्राप्त होते हैं, और प्रति सेकंड 1 मिलियन तक रीड्स को सर्व किया जाता है। खिलाड़ियों को ग्लोबल टॉप 100, उनकी सटीक रैंक, और दोनों तरफ के 10 खिलाड़ियों को देखने की आवश्यकता होती है। सबमिट किया गया परिणाम 5 सेकंड के भीतर दिखाई देना चाहिए, और रीड p99 100 मिलीसेकंड से कम रहना चाहिए। स्कोर 0..1,000,000 में पूर्णांक (integers) हैं, और प्रत्येक खिलाड़ी सीज़न के लिए केवल अपना सर्वश्रेष्ठ स्कोर रखता है। टाई होने पर रैंक साझा की जाती है, इसलिए रैंक 1, 2, 2 के बाद 4 आती है। टाई हुए खिलाड़ियों का डिस्प्ले ऑर्डर player_id द्वारा स्थिर होता है, लेकिन वह ऑर्डर उनकी रैंक नहीं बदलता है।
स्केल, लेटेंसी और स्कोर रेंज इंटरव्यू के अनुमान हैं। कोई क्लाइंट विश्वसनीय स्कोर घोषित नहीं कर सकता। लीडरबोर्ड द्वारा उपभोग किए जा सकने वाले इवेंट को जनरेट करने से पहले मैच सर्विस रिज़ल्ट वैलिडेशन और एंटी-चीट जाँच पूरी करती है। यह समस्या ऑर्डर्ड इंडेक्सिंग, रीड्स और राइट्स के सेपरेशन, हॉट-की शार्डिंग, कंसिस्टेंसी, रीबिल्डेबिलिटी और सीज़न लाइफ़साइकिल का परीक्षण करती है। यह एंटी-चीट मॉडल की मांग नहीं करती है।
एक सार्वजनिक तकनीकी साक्षात्कार गाइड में सिस्टम डिज़ाइन उदाहरण के रूप में "गेम के लिए लीडरबोर्ड डिज़ाइन करना" सूचीबद्ध है और कहा गया है कि यह रैंकिंग लॉजिक, रीड/राइट परफ़ॉर्मेंस और रियल-टाइम अपडेट का परीक्षण करता है। 2026 में प्रकाशित एक Redis लीडरबोर्ड ट्यूटोरियल भी स्कोर अपडेट करने, Top-N प्राप्त करने और रैंक लुकअप के लिए बुनियादी Sorted Set पाथ को प्रदर्शित करता है। सार्वजनिक साक्ष्य किसी विश्वसनीय कंपनी एट्रिब्यूशन को स्थापित नहीं करते हैं, इसलिए companyName रिक्त (null) रहता है।
इंटरव्यूअर किन बातों का मूल्यांकन कर रहा है
पहला संकेत यह है कि क्या उम्मीदवार "रैंक" को परिभाषित करता है। उच्च स्कोर पहले होना आसान है; यह तय करना कि क्या टाई होने पर रैंक साझा की जाती है, सबसे पहले हासिल करने वाले को प्राथमिकता दी जाती है, या खिलाड़ी आईडी पर टाई तोड़ा जाता है, सीधे डेटा मॉडल को बदल देता है। इस अनुबंध (contract) के बिना, दो सेवाएँ समान खिलाड़ियों के लिए अलग-अलग रैंक उत्पन्न कर सकती हैं।
दूसरा संकेत यह पहचानना है कि एक Sorted Set एक ऑर्डर्ड कलेक्शन को हल करता है। Redis ZADD अपडेट और ZREVRANK रैंक लुकअप को O(log N) के रूप में दस्तावेज़ित करता है, जो एक मध्यम लीडरबोर्ड के लिए उपयोगी है। हालाँकि, Redis Cluster की (key) के आधार पर हैश स्लॉट असाइन करता है। केवल अधिक क्लस्टर नोड्स जोड़े जाने के कारण ग्लोबल बोर्ड को एक ही की में रखने से उसके 50 मिलियन मेंबर्स विभाजित नहीं होते हैं। एक मज़बूत उत्तर पहले सरल पाथ को बनाए रखता है, फिर एप्लिकेशन-लेवल शार्डिंग केवल तब जोड़ता है जब एक की मेमोरी, राइट या रिकवरी बजट से अधिक हो जाती है।
तीसरा संकेत "5 सेकंड के भीतर सटीक" को एक कंसिस्टेंट रीड वर्ज़न में बदलना है। किसी खिलाड़ी को एक स्कोर बकेट से दूसरे में ले जाने से रिमूव-फिर-ऐड के साथ एक मध्यवर्ती गायब होना, या ऐड-फिर-रिमूव के साथ डुप्लिकेट बनता है। एक पल से बकेट काउंट और दूसरे पल से बकेट ऑर्डर पढ़ने से भी गलत रैंक उत्पन्न होती है। एक टेस्टेबल डिज़ाइन एक अनुरोध को पूरी तरह से प्रकाशित एक वर्ज़न को रीड करवाता है।
अंत में, डिज़ाइन ऑपरेट करने योग्य होना चाहिए। स्कोर इवेंट्स को इडेम्पोटेंसी की आवश्यकता होती है, सुधारों (corrections) को स्कोर कम करने में सक्षम होना चाहिए, सीज़न क्लोज़ को एक कंसिस्टेंट स्नैपशॉट फ्रीज करना चाहिए, और एक खोए हुए कैश को एक विश्वसनीय लेज़र से रीबिल्ड करने योग्य होना चाहिए। केवल "game service → Redis" वाला आरेख डुप्लिकेट्स, हॉटस्पॉट्स, डेटा हानि, रिवोक किए गए चीटिंग स्कोर्स या सेटलमेंट विवादों को संबोधित नहीं करता है।
उत्तर देने से पहले स्पष्ट करने वाले प्रश्न
- क्या स्कोर संचयी (cumulative), सर्वश्रेष्ठ (best), या नवीनतम (latest) है? यह प्रॉम्प्ट सीज़नल सर्वश्रेष्ठ रखता है। एक संचयी स्कोर इवेंट की पहचान और एटॉमिक इन्क्रीमेंट्स को महत्वपूर्ण बनाता है क्योंकि डुप्लिकेट इवेंट अन्यथा दो बार जुड़ जाते हैं। यदि निर्णायक स्कोर को सही कर सकते हैं, तो एपीआई को केवल
ZINCRBYके बजाय वर्ज़न्ड एब्सोल्यूट वैल्यूज़ की आवश्यकता होती है। - टाई को कैसे रैंक किया जाता है? यह प्रॉम्प्ट प्रतियोगिता रैंकिंग (competition ranking) का उपयोग करता है: केवल कड़ाई से उच्च स्कोर गिने जाते हैं, और टाई होने वाले खिलाड़ी समान रैंक साझा करते हैं। "पहले हासिल करने वाला जीतता है" के लिए सॉर्ट की में विश्वसनीय उपलब्धि समय की आवश्यकता होती है; ZSET का डिफ़ॉल्ट लेक्सिकोग्राफ़िकल टाई ऑर्डर वह नियम स्वचालित रूप से प्रदान नहीं करता है।
- सटीकता और ताज़गी (freshness) की क्या आवश्यकताएँ हैं? प्रकाशित वर्ज़न सटीक होने चाहिए, जिसमें 5 सेकंड तक का पुरानापन (staleness) हो सकता है। लीनियरिज़ेबल तत्काल दृश्यता राइट पाथ पर क्रॉस-शार्ड कोऑर्डिनेशन डाल देगी। अनुमानित (approximate) रैंक सैंपल्स या क्वांटाइल स्केच और बहुत सरल डिज़ाइन की अनुमति देगा।
- किन बोर्ड्स की आवश्यकता है? मुख्य पाथ ग्लोबल सीज़नल बोर्ड है। क्षेत्रों के एक छोटे निश्चित सेट में अलग मटीरियलाइज़्ड व्यू हो सकते हैं। फ्रेंड्स बोर्ड आमतौर पर एक बैच में दोस्तों के स्कोर प्राप्त करता है और प्रति खिलाड़ी एक बोर्ड बनाए रखने के बजाय प्रति अनुरोध उन्हें सॉर्ट करता है।
- किन रीड्स का समर्थन किया जाता है? Top 100, व्यक्तिगत रैंक, और 20-खिलाड़ियों का पड़ोस (neighborhood)। अनबाउंस्ड डीप पेजिनेशन महंगे स्कैन बनाता है, इसलिए विंडोज़ सीमित होनी चाहिए और एक वर्ज़न्ड कर्सर का उपयोग करना चाहिए। एक समाप्त हो चुके वर्ज़न को रिलोकेशन की आवश्यकता होती है।
- स्कोर की पुष्टि कौन करता है? केवल एक विश्वसनीय मैच-रिज़ल्ट्स सर्विस राइट करती है। एक क्लाइंट सबमिशन गेम इनपुट है, न कि ऐसा स्कोर जिसे सीधे ऑर्डर्ड इंडेक्स में डाला जा सके।
- सीज़न कब बंद होता है? सर्वर इवेंट समय और एक स्पष्ट कटऑफ़ का उपयोग करें। उत्पाद नीति में यह बताया जाना चाहिए कि क्या देर से आए परिणामों को अस्वीकार किया जाता है, समीक्षा की जाती है, या कहीं और सौंपा जाता है; एक बैकग्राउंड जॉब पुरस्कृत स्नैपशॉट को चुपचाप म्यूटेट नहीं कर सकती।
- टिकाऊ (durable) क्या है? एक रैंक व्यू कुछ समय के लिए बासी हो सकता है या रीबिल्ड किया जा सकता है। विश्वसनीय स्कोर लेज़र और अंतिम सीज़न स्नैपशॉट कैश विफलता के साथ गायब नहीं हो सकते।
30-सेकंड उत्तर की रूपरेखा
"मैं रैंक को 1 + खिलाड़ी के स्कोर से अधिक स्कोर वाले खिलाड़ियों की संख्या के रूप में परिभाषित करूँगा, ताकि टाई होने वाले खिलाड़ी एक रैंक साझा करें। एक विश्वसनीय मैच सर्विस एक event_id और प्लेयर वर्ज़न के साथ एक एब्सोल्यूट बेस्ट स्कोर राइट करती है। टिकाऊ स्कोर टेबल सोर्स ऑफ ट्रुथ है, और इसका चेंज स्ट्रीम लीडरबोर्ड को मटीरियलाइज़ करता है। छोटे पैमाने पर, एक Redis Sorted Set ZADD, Top-N और रिवर्स रैंक का समर्थन करता है। एक बार जब 50 मिलियन खिलाड़ियों के लिए ग्लोबल हॉट की सिंगल-शार्ड बजट से अधिक हो जाती है, तो मैं इसे फिक्स्ड स्कोर रेंज द्वारा पार्टिशन करूँगा। एक व्यक्तिगत रैंक सभी उच्च बकेट्स में काउंट, प्लस खिलाड़ी के बकेट में उच्च स्कोर का काउंट, प्लस वन होती है।
"एक क्रॉस-बकेट मूव को बीच में उजागर नहीं किया जा सकता है। इसलिए मटीरियलाइज़र्स हर कुछ सेकंड में एक वर्ज़न कमिट करते हैं। सभी बकेट्स, काउंट प्रीफिक्स और Top 100 तैयार होने के बाद, एक कोऑर्डिनेटर वर्तमान मैनिफ़ेस्ट को एटॉमिक रूप से स्वैप करता है। प्रतिक्रियाओं में leaderboard_version और as_of शामिल हैं। राइट्स इवेंट और वर्ज़न द्वारा इडेम्पोटेंट हैं, कैश स्कोर लेज़र से रीबिल्ड करने योग्य है, और सीज़न क्लोज़ रिवॉर्ड जारी होने से पहले एक पूर्ण वर्ज़न को फ़्रीज़ और रीकंसाइल करता है।"
चरण-दर-चरण गहन विश्लेषण
चरण 1: स्टोरेज चुनने से पहले APIs और ऑर्डरिंग कॉन्ट्रैक्ट को तय करें।
इंटरनल राइट पाथ को पब्लिक रीड्स से अलग करें:
POST /internal/v1/seasons/{season_id}/scores:apply
GET /v1/seasons/{season_id}/leaderboard/top?limit=100
GET /v1/seasons/{season_id}/players/{player_id}/rank
GET /v1/seasons/{season_id}/players/{player_id}/neighbors?radius=10&version=...राइट में event_id, match_id, player_id, एब्सोल्यूट best_score, score_version, और एक विश्वसनीय पूरा होने का समय शामिल है। केवल मैच-रिज़ल्ट्स पहचान अधिकृत है। एक रीड leaderboard_version, as_of, स्कोर, रैंक और मेंबर्स लौटाता है। एक नेबरहुड अनुरोध को पहली प्रतिक्रिया से वर्ज़न को बनाए रखना चाहिए; अन्यथा पेजिंग करते समय रैंक परिवर्तन खिलाड़ियों को डुप्लिकेट या छोड़ सकते हैं।
चार इनवेरिएंट्स डिज़ाइन को संचालित करते हैं: प्रत्येक (season_id, player_id) एक वर्ज़न में एक बार होता है; प्रकाशित खिलाड़ी स्कोर ऑर्डर्ड इंडेक्स से मेल खाता है; रैंक हमेशा 1 + count(score > my_score) के बराबर होती है; और एक मैनिफ़ेस्ट केवल उन शार्ड वर्ज़न्स को संदर्भित करता है जो सभी समाप्त हो चुके हैं।
चरण 2: विश्वसनीय स्कोर टेबल को सोर्स ऑफ ट्रुथ बनाएं।
मैच-सेटलमेंट सर्विस एक इम्यूटेबल रिज़ल्ट लेज़र लिखने से पहले ऑथराइजेशन, मैच स्टेट और एंटी-चीट रिज़ल्ट्स को वैलिडेट करती है। एक डेटाबेस ट्रांजेक्शन में, लीडरबोर्ड राइटर event_id को डिडुप्लिकेट करता है और प्लेयर रो को सशर्त रूप से अपडेट करता है। यह एक पुराने वर्ज़न को अस्वीकार करता है, एक समान पुनर्रयास के लिए पिछला परिणाम देता है, और सामान्य बेस्ट को केवल तब बदलता है जब new_score > best_score हो। चीटिंग स्कोर को रद्द करने पर एक उच्च score_version और एक सही एब्सोल्यूट वैल्यू लिखी जाती है, जिससे कमी भी कन्वर्ज हो जाती है।
कमिट के बाद, एक आउटबॉक्स या समकक्ष टिकाऊ चेंज स्ट्रीम उत्सर्जित करती है:
ScoreChanged {
season_id, player_id, old_score, new_score,
score_version, event_id, committed_at
}इवेंट्स खिलाड़ी द्वारा पार्टिशन किए जाते हैं, और एक मटीरियलाइज़र केवल उस खिलाड़ी के वर्तमान वर्ज़न से नए बदलाव को लागू करता है। डुप्लिकेट डिलीवरी दो बार अंक नहीं जोड़ सकती है, और देर से आया पुराना इवेंट नए स्कोर को ओवरराइट नहीं कर सकता है। लीडरबोर्ड एक डिस्पोजेबल मटीरियलाइज़्ड व्यू है। लेज़र और करंट-स्कोर टेबल रीबिल्ड के स्रोत हैं।
चरण 3: पहले सिंगल-Sorted-Set डिज़ाइन प्रस्तुत करें।
जब मेंबरशिप, पीक राइट्स, मेमोरी और रिकवरी समय एक शार्ड में फिट होते हैं, तो प्रति सीज़न एक ZSET सही शुरुआती बिंदु है:
key = leaderboard:{season_id}
member = player_id
score = best_scoreZADD मौजूदा मेंबर के लिए एक नया स्कोर सेट करता है और इसे रिपोजिशन करता है। एक डिसेंडिंग रेंज Top-N लौटाती है, और ZREVRANK एक स्थिति लौटाता है। Redis अपडेट को O(log N), एक निश्चित आकार की रेंज को O(log N + M), और रैंक लुकअप को O(log N) के रूप में दस्तावेज़ित करता है। प्रॉम्प्ट की पूर्णांक सीमा 0..1,000,000, 2^53 सीमा से बहुत नीचे है जिसके माध्यम से एक डबल पूर्णांकों को सटीक रूप से दर्शाता है।
समान-स्कोर वाले मेंबर्स को मेंबर वैल्यूज़ के बाइनरी लेक्सिकोग्राफ़िकल ऑर्डर द्वारा क्रमित किया जाता है। यह केवल उस अनुबंध में फिट बैठता है जहाँ टाई होने पर रैंक साझा की जाती है और ID डिस्प्ले ऑर्डर को नियंत्रित करती है। किसी अनपेक्षित स्कोर और मिलीसेकंड टाइमस्टैम्प को एक फ्लोटिंग-पॉइंट वैल्यू में पैक न करें और यह न मानें कि कंपोजिट ऑर्डरिंग सही रहेगी। ZREVRANK + 1 भी साझा व्यावसायिक रैंक नहीं है क्योंकि टाई वाले मेंबर्स को अलग-अलग स्थान मिलते हैं। व्यावसायिक फ़ॉर्मूला 1 + count(score > my_score) है। एक ZSET के अंदर, ZCOUNT key (my_score +inf उस काउंट की गणना करता है; ( निचली सीमा को अनन्य (exclusive) बनाता है। डिस्प्ले स्थिति को व्यावसायिक रैंक से अलग रखें।
चरण 4: सिद्ध करें कि बड़े पैमाने पर एप्लिकेशन-लेवल शार्डिंग की आवश्यकता क्यों है।
Redis Cluster कीज़ को हैश स्लॉट में मैप करता है, और एक स्थिर स्लॉट एक प्राइमरी द्वारा सर्व किया जाता है। की leaderboard:{season} एक ही की रहती है, इसलिए इसके मेंबर्स, राइट्स और रिकवरी लोड एक स्लॉट के प्राइमरी पर केंद्रित रहते हैं। hash(player_id) द्वारा शार्डिंग राइट्स को संतुलित करती है लेकिन ग्लोबल रैंक को महंगा बनाती है क्योंकि प्रत्येक अनुरोध को सभी प्लेयर शार्ड्स में मर्ज या काउंट करना पड़ता है।
इस प्रॉम्प्ट में एक सीमित स्कोर सीमा है, इसलिए फिक्स्ड स्कोर-रेंज बकेट्स उपयोगी हैं। प्रति बकेट 10,000-पॉइंट रेंज के साथ, 101 बकेट्स हैं:
bucket_id = floor(score / 10,000)
rank(player) = 1
+ count(all buckets with a higher bucket_id)
+ count(score > player_score inside the player's bucket)प्रत्येक बकेट ऑर्डर्ड रहती है, और बकेट कीज़ विभिन्न स्लॉट्स पर कब्जा कर सकती हैं। प्रत्येक वर्ज़न के लिए, बकेट काउंट और एक हाई-टू-लो प्रीफिक्स सम स्टोर करें। व्यक्तिगत रैंक के लिए फिर एक प्लेयर लुकअप, एक प्रीफिक्स वैल्यू, और बकेट के अंदर कड़ाई से उच्च काउंट की आवश्यकता होती है। Top 100 केवल उच्चतम गैर-रिक्त बकेट्स को स्कैन करता है और इसे अलग से मटीरियलाइज़ किया जाता है। फिक्स्ड रेंज एक हॉट हाई-स्कोर बकेट बना सकती हैं। मापे जाने पर उस रेंज को और विभाजित करें, लेकिन सीमाओं को वर्ज़न्ड मैनिफ़ेस्ट में रखें ताकि रीडर्स और राइटर्स एक ही लेआउट का उपयोग करें।
चरण 5: क्रॉस-बकेट एटॉमीसिटी को हल करने के लिए वर्ज़न्ड पब्लिकेशन का उपयोग करें।
जब कोई खिलाड़ी 39,000 से 51,000 पर जाता है, तो सिस्टम उन्हें बकेट 3 से हटा देता है, उन्हें बकेट 5 में जोड़ता है, और दो काउंट्स बदलता है। Redis स्लॉट्स में रिमूव, ऐड और काउंटर परिवर्तन एक साधारण एटॉमिक ऑपरेशन नहीं हैं। मध्यवर्ती स्थिति को उजागर करने से डुप्लिकेट, गायब होना, या ऑफ़-बाय-वन ग्लोबल रैंक उत्पन्न होती है।
इसलिए मटीरियलाइज़र्स छोटे युगों (epochs) में लॉजिकल स्नैपशॉट कमिट करते हैं। प्रत्येक रैंक शार्ड वर्तमान प्रकाशित वर्ज़न से शुरू होता है, एक इडेम्पोटेंट बैच लागू करता है, और अगला ऑर्डर्ड इंडेक्स और काउंट तैयार करता है। एक बार जब प्रत्येक शार्ड पूरा होने की रिपोर्ट करता है, तो कोऑर्डिनेटर कुल मेंबरशिप, परिवर्तन काउंट और शार्ड चेकसम की जाँच करता है, फिर एटॉमिक रूप से current_manifest को v से v+1 पर ले जाता है। एक रीड पहले मैनिफ़ेस्ट प्राप्त करता है और उस वर्ज़न को सभी सबक्वेरीज़ में ले जाता है। एक अधूरा वर्ज़न अदृश्य रहता है, विफल शार्ड्स पुनः प्रयास कर सकते हैं, और पिछला वर्ज़न तब तक रहता है जब तक इन-फ़्लाइट रीड्स समाप्त नहीं हो जाते।
लॉजिकल स्नैपशॉट MVCC, कॉपी-ऑन-राइट पेजों, या बेस प्लस डेल्टा के माध्यम से पुराने डेटा का पुन: उपयोग कर सकते हैं, जिससे एक इम्यूटेबल बाहरी वर्ज़न को संरक्षित करते हुए हर कुछ सेकंड में 50 मिलियन मेंबर्स की पूरी कॉपी से बचा जा सकता है। इपोक अवधि, अप्लाई लैग, और पब्लिश लैग को एक साथ 5 सेकंड से कम रहना चाहिए। यदि वे ऐसा नहीं करते हैं, तो बासीपन (staleness) की रिपोर्ट करें और आधे-अधूरे वर्ज़न को प्रकाशित करने के बजाय रियल-टाइम सेवा का दावा करना बंद करें।
चरण 6: ग्लोबल, रीजनल और फ्रेंड्स बोर्ड्स को अलग करें।
ग्लोबल बोर्ड बकेटेड इंडेक्स का उपयोग करता है। क्षेत्रों का एक छोटा निश्चित सेट समान स्कोर इवेंट से स्वतंत्र (season, region) व्यू बनाए रख सकता है। क्षेत्र सर्वर द्वारा बनाए रखे गए प्लेयर-प्रोफाइल वर्ज़न से आता है ताकि क्लाइंट अनुरोध के दौरान क्षेत्रों को स्विच न कर सके। केवल ग्लोबल Top-N को फ़िल्टर करने से ग्लोबल पेज के बाहर का एक मजबूत रीजनल खिलाड़ी छूट जाएगा; एक रीजनल इंडेक्स का उपयोग करें या परिणाम को अनुमानित लेबल करें।
फ्रेंड्स बोर्ड आम तौर पर छोटा होता है। सोशल ग्राफ़ से वर्ज़न्ड फ्रेंड आईडी प्राप्त करें, उसी लीडरबोर्ड वर्ज़न पर उनके स्कोर को बैच-रीड करें, फिर score DESC, player_id ASC द्वारा सॉर्ट करें और एप्लिकेशन सर्विस में टाई की गणना करें। प्रति खिलाड़ी एक फ्रेंड्स ZSET बनाए रखने से अत्यधिक राइट एम्प्लीफिकेशन होता है: एक स्कोर परिवर्तन हर दोस्त के बोर्ड में फैन आउट होता है, और संबंध परिवर्तनों के लिए बैकफ़िल की आवश्यकता होती है।
चरण 7: ट्रैफ़िक का अनुमान लगाएं, फिर मापों से शार्ड्स का आकार निर्धारित करें।
यदि इसके एनवेलप सहित एक टिकाऊ स्कोर इवेंट 128 बाइट्स का है, तो पीक लॉजिकल राइट लोअर बाउंड है:
200,000 events/s × 128 bytes = 25.6 MB/s
25.6 MB/s × 86,400 s = 2.21184 TB/day
three-replica log lower bound = 6.63552 TB/dayयह अनकंप्रेस्ड लोअर बाउंड इंडेक्स, प्रोटोकॉल ओवरहेड, बैच, पुनः प्रयास और रेप्लिकेट रिकवरी को बाहर करता है। केवल एक इवेंट जो सर्वश्रेष्ठ स्कोर में सुधार करता है या इसे सही करता है, रैंकिंग को बदलता है, लेकिन प्रत्येक विश्वसनीय इवेंट को अभी भी लेज़र-साइड डिडुप्लिकेशन और ऑडिट की आवश्यकता होती है। प्रति सेकंड दस लाख रीड्स सभी रैंक शार्ड्स तक नहीं पहुँच सकते। वर्ज़न द्वारा Top 100 को कैश करें। एक व्यक्तिगत परिणाम एक छोटे TTL का उपयोग कर सकता है, लेकिन की में खिलाड़ी और वर्ज़न शामिल होता है; शार्ड फिर नेबरहुड विंडोज़ को बैच-रीड करता है।
किसी लेख से निश्चित शार्ड काउंट की नकल न करें। 50 मिलियन प्रोडक्शन-आकार की पंक्तियों के साथ मेंबर मेमोरी, ZADD, कड़ाई से उच्च काउंट्स, रेंज रीड्स और स्नैपशॉट निर्माण का बेंचमार्क करें। p50/p95/p99, CPU, मेमोरी, रेप्लिकेशन लैग और रिकवरी समय रिकॉर्ड करें, फिर पीक राइट्स और विफलता हेडरूम से बकेट स्प्लिट्स और नोड काउंट प्राप्त करें। यदि एक ZSET हर बजट के अंदर रहता है, तो यह कस्टम बकेट कोऑर्डिनेटर की तुलना में अधिक विश्वसनीय है।
चरण 8: सीज़न बंद करें और व्यू को रीबिल्ड करने योग्य रखें।
सीज़न के CLOSING में प्रवेश करने के बाद, कटऑफ से पहले के पहले से स्वीकृत परिणाम स्ट्रीम के माध्यम से जारी रहते हैं, जबकि नए अयोग्य परिणामों को अस्वीकार कर दिया जाता है। एक इनपुट हाई वॉटरमार्क रिकॉर्ड करें। एक बार जब प्रत्येक मटीरियलाइज़र उस तक पहुँच जाता है, तो एक उम्मीदवार अंतिम वर्ज़न बनाएं। सेटलमेंट कुल खिलाड़ियों, बकेट काउंट के योग, Top 100, यादृच्छिक सैंपल वाली रैंक, डुप्लिकेट-प्लेयर काउंट और प्रत्येक शार्ड चेकसम की तुलना करता है। सफलता पर, मैनिफ़ेस्ट को FINAL चिह्नित करें; रिवॉर्ड सेवा केवल उस इम्यूटेबल वर्ज़न को पढ़ती है। बाद के विवाद पुरस्कृत बोर्ड को चुपचाप फिर से लिखने के बजाय एक ऑडिट किए गए सुधार प्रवाह में प्रवेश करते हैं।
यदि कोई रैंकिंग कैश या संपूर्ण क्लस्टर खो जाता है, तो वर्तमान-स्कोर टेबल या इम्यूटेबल लेज़र को score_version द्वारा एक नए नेमस्पेस में रीप्ले करें। एक पूर्ण उम्मीदवार का निर्माण और रीकंसाइल करें, फिर एटॉमिक रूप से मैनिफ़ेस्ट को स्वैप करें। रीबिल्ड के दौरान पुराना वर्ज़न केवल-पठन (read-only) रहता है। यदि कोई मौजूद नहीं है, तो आंशिक बोर्ड को पूर्ण के रूप में प्रस्तुत करने के बजाय एक स्पष्ट रूप से अस्थायी रूप से अनुपलब्ध या बासी स्थिति लौटाएं।
फॉल्ट मैट्रिक्स में डुप्लिकेट और आउट-ऑफ-ऑर्डर इवेंट्स शामिल हैं; क्रॉस-बकेट वृद्धि और नीचे की ओर सुधार; मध्य-युग में एक शार्ड क्रैश; मैनिफ़ेस्ट स्वैप से पहले और बाद में एक कोऑर्डिनेटर क्रैश; एक कैश नोड की हानि; Top-100 सीमा पर भारी टाई; 1 मिलियन रीड QPS; कटऑफ के आसपास देर से आने वाले परिणाम; और शैडो तुलना के साथ पूर्ण रीबिल्ड। लगातार चार इनवेरिएंट्स की पुष्टि करें और इवेंट-टू-पब्लिश p99, वर्ज़न आयु, बकेट स्क्यू, अस्वीकृत इवेंट्स, रीबिल्ड प्रगति और चेकसम विफलताओं की निगरानी करें।
प्रभावशाली नमूना उत्तर
"मैं पहले रैंक को परिभाषित करूँगा। टाई होने वाले खिलाड़ी यहाँ एक रैंक साझा करते हैं, इसलिए एक खिलाड़ी की रैंक 1 + खिलाड़ी के स्कोर से अधिक स्कोर वाले
खिलाड़ियों की संख्या है; Top-100 डिस्प्ले स्थिति और व्यावसायिक रैंक अलग हैं। क्लाइंट एक विश्वसनीय स्कोर नहीं लिख सकते हैं। मैच वैलिडेशन के बाद, लीडरबोर्ड राइटर इवेंट आईडी को डिडुप्लिकेट करता है और प्लेयर स्कोर वर्ज़न द्वारा सशर्त रूप से एक एब्सोल्यूट बेस्ट सेट करता है। करंट-स्कोर टेबल सोर्स ऑफ ट्रुथ है, और इसका कमिटेड चेंज स्ट्रीम रैंक व्यू को चलाता है।
"यदि बोर्ड एक Redis शार्ड में फिट बैठता है, तो मैं प्रति सीज़न एक Sorted Set से शुरुआत करूँगा। स्कोर सेट करना, Top-N पढ़ना, और स्कोर द्वारा काउंट करना सीधा है। लेकिन 50 मिलियन खिलाड़ियों के लिए ग्लोबल बोर्ड एक हॉट की है, और Redis Cluster एक की के भीतर मेंबर्स के बजाय की द्वारा शार्ड करता है। चूंकि स्कोर सीमा 0..1,000,000 पर बंधी है, इसलिए मैं 101 फिक्स्ड स्कोर-रेंज बकेट्स में स्केल करूँगा। व्यक्तिगत रैंक उच्च-बकेट काउंट प्रीफिक्स प्लस स्थानीय बकेट में कड़ाई से उच्च खिलाड़ी प्लस एक है। Top 100 उच्चतम गैर-रिक्त बकेट्स से आता है।
"एक क्रॉस-बकेट मूव दो कीज़ और काउंट्स को बदलता है। जगह में अपडेट करने से मध्यवर्ती स्थिति उजागर होगी, इसलिए मैं अधिकतम कुछ सेकंड के युगों (epochs) में अगला इंडेक्स बनाऊँगा। प्रत्येक शार्ड द्वारा इडेम्पोटेंट परिवर्तन लागू करने और मेंबरशिप/चेकसम समाधान पास होने के बाद ही कोऑर्डिनेटर एटॉमिक रूप से वर्तमान मैनिफ़ेस्ट को स्वैप करता है। प्रत्येक प्रतिक्रिया में वर्ज़न और as-of शामिल होता है, और नेबरहुड रीड्स उस वर्ज़न को बनाए रखते हैं। प्रकाशित वर्ज़न 5 सेकंड तक के बासीपन की कीमत पर सटीक होते हैं।
"प्रति इवेंट 128 बाइट्स पर, पीक राइट्स 25.6 MB/s लॉजिकल ट्रैफ़िक हैं, जो प्रति दिन लगभग 2.21 TB है, और एक थ्री-रेप्लिकेट लॉग लोअर बाउंड लगभग 6.64 TB है। इंडेक्स नोड्स और बकेट काउंट अभी भी प्रोडक्शन-आकार के डेटा पर एक बेंचमार्क से आते हैं। वर्ज़न द्वारा Top 100 को कैश करें। दोस्तों के स्कोर को बैच-फ़ेच करके और स्थानीय रूप से सॉर्ट करके फ्रेंड्स बोर्ड बनाएं, जिससे प्रत्येक स्कोर परिवर्तन पर फैन-आउट से बचा जा सके।
"सीज़न बंद होने पर, मैं एक इनपुट हाई वॉटरमार्क रिकॉर्ड करूँगा, सभी शार्ड्स के कैच अप होने की प्रतीक्षा करूँगा, और एक उम्मीदवार अंतिम वर्ज़न को फ्रीज करूँगा। रिवॉर्ड सेवा द्वारा FINAL मैनिफ़ेस्ट पढ़ने से पहले मैं मेंबरशिप, बकेट सम, Top 100, सैंपल की गई रैंक और शार्ड चेकसम को रीकंसाइल करूँगा। एक खोया हुआ कैश विश्वसनीय स्कोर टेबल या लेज़र से रीबिल्ड किया जाता है। फॉल्ट टेस्ट डुप्लिकेट/आउट-ऑफ-ऑर्डर इवेंट्स, क्रॉस-बकेट सुधार, शार्ड और कोऑर्डिनेटर क्रैश, भारी टाई, कटऑफ रेस और पूर्ण रीबिल्ड को कवर करते हैं।"
सामान्य गलतियाँ
- क्लाइंट से सीधे स्कोर स्वीकार करना → ऑर्डरिंग स्टोर यह निर्धारित नहीं कर सकता कि यह मान्य है या नहीं → केवल एक विश्वसनीय मैच सेवा द्वारा पुष्टि किए गए ऑडिट किए गए परिणामों का उपभोग करें।
- टाई रैंक के लिए
ZREVRANK + 1का उपयोग करना → एक ZSET समान स्कोर को अलग लेक्सिकोग्राफ़िकल स्थितियां देता है, जिससे साझा-रैंक अनुबंध का उल्लंघन होता है →1 + count(score > my_score)की गणना करें। - फ्लोटिंग-पॉइंट स्कोर में लापरवाही से टाइमस्टैम्प पैक करना → डबल्स में एक सटीकता सीमा होती है और एक कंपोजिट एन्कोडिंग की प्राथमिकता को उलट सकती है → पहले टाई को परिभाषित करें, फिर एक सिद्ध पूर्णांक एन्कोडिंग या कंपोजिट-ऑर्डर इंडेक्स का उपयोग करें।
- यह मान लेना कि अधिक Redis Cluster नोड्स एक ग्लोबल-बोर्ड की को विभाजित करते हैं → क्लस्टर एक की का हैश स्लॉट असाइन करता है, और एकल की एक स्लॉट प्राइमरी पर रहती है → सिंगल-की सीमा को मापें, फिर एक मर्ज करने योग्य व्यावसायिक आयाम द्वारा शार्ड करें।
- खिलाड़ियों को हैश-शार्ड करना और प्रत्येक रैंक क्वेरी को फैन आउट करना → क्वेरी लागत शार्ड काउंट के साथ बढ़ती है और 1 मिलियन रीड QPS पर फट जाती है → सीमित स्कोर सीमा पर काउंट्स का उपयोग करें, या अनुमति मिलने पर अनुमानित रैंक लौटाएं।
- बकेट्स में रिमूव फिर ऐड, या ऐड फिर रिमूव करना → रीडर्स को गायब होना, डुप्लिकेट या गलत काउंट दिखाई देता है → एक पूर्ण वर्ज़न बनाएं और इसे मैनिफ़ेस्ट के माध्यम से एटॉमिक रूप से प्रकाशित करें।
- प्रत्येक परिणाम के लिए
ZINCRBYका उपयोग करना → डुप्लिकेट इवेंट दो बार जुड़ते हैं और चीटिंग-स्कोर निरस्तीकरण सुरक्षित रूप से कम नहीं हो सकता है → इवेंट और प्लेयर वर्ज़न द्वारा एब्सोल्यूट स्कोर को इडेम्पोटेंट रूप से सेट करें। - कटऑफ पर लाइव कैश से रिवॉर्ड जारी करना → स्वीकृत प्री-कटऑफ इवेंट्स इन-फ़्लाइट रह सकते हैं, और कैश टिकाऊ सत्य नहीं है → एक हाई वॉटरमार्क रिकॉर्ड करें, कैच अप करें, रीकंसाइल करें और एक FINAL वर्ज़न फ्रीज करें।
- केवल यह सत्यापित करना कि Top 100 सही दिखता है → एक बकेट-काउंट त्रुटि या डुप्लिकेट खिलाड़ी प्रत्येक लॉन्ग-टेल रैंक को स्थानांतरित कर सकता है → मेंबरशिप संरक्षण, विशिष्टता, सैंपल किए गए रैंक फ़ार्मुलों, शार्ड चेकसम और पूर्ण रीबिल्ड को सत्यापित करें।
फॉलो-अप प्रश्न और उत्तर
फॉलो-अप 1: क्या हम पांच-सेकंड के स्नैपशॉट को बनाए रख सकते हैं यदि किसी खिलाड़ी को तुरंत राइट को पढ़ना हो?
राइटर के रीड-योर-राइट्स अनुभव को विश्व स्तर पर प्रकाशित रैंक से अलग करें। राइट रिस्पॉन्स कमिट किए गए नए स्कोर और पेंडिंग वर्ज़न को लौटा सकता है। एक तत्काल रीफ्रेश यह कह सकता है कि स्कोर की पुष्टि हो गई है और ग्लोबल रैंक अपडेट हो रही है, या नया व्यक्तिगत स्कोर दिखा सकता है जबकि ग्लोबल रैंक अभी भी नवीनतम पूर्ण मैनिफ़ेस्ट को संदर्भित करती है। यदि उत्पाद को उसी सिंक्रोनस प्रतिक्रिया में नए स्कोर और सटीक ग्लोबल रैंक की आवश्यकता होती है, तो अपडेट को विश्व स्तर पर समन्वित ऑर्डरिंग पाथ में प्रवेश करना होगा। राइट लेटेंसी और विफलता कपलिंग बढ़ जाती है, इसलिए मूल थ्रूपुट लक्ष्य का पुनर्मूल्यांकन किया जाना चाहिए।
फॉलो-अप 2: क्या होगा यदि उच्चतम स्कोर बकेट में 10 मिलियन खिलाड़ी जमा हो जाएं?
बकेट लेआउट वर्ज़न्ड मेटाडेटा है। पहले राइट QPS, मेंबरशिप, CPU और p99 के माध्यम से हॉटस्पॉट साबित करें, फिर उस स्कोर रेंज को संकीर्ण सब-बकेट्स में विभाजित करें। चूंकि साझा रैंक कड़ाई से उच्च स्कोर पर निर्भर करती है, इसलिए एक सटीक स्कोर को मनमाने ढंग से खिलाड़ी-शार्ड नहीं किया जा सकता है और स्थानीय रैंक का योग नहीं किया जा सकता है; इसका कुल काउंट एग्रीगेटेड रहना चाहिए। समानांतर में नया लेआउट बनाएं, उसी इनपुट हाई वॉटरमार्क पर मेंबरशिप और सैंपल की गई रैंक की तुलना करें, और फिर नए लेआउट को संदर्भित करने वाला मैनिफ़ेस्ट प्रकाशित करें।
फॉलो-अप 3: यदि टाई स्कोर तक पहुँचने वाला सबसे पहला खिलाड़ी जीतता है तो क्या बदलता है?
रैंक अब केवल-स्कोर काउंट नहीं है। सॉर्ट की (score DESC, achieved_at ASC, player_id ASC) बन जाती है, जिसमें achieved_at विश्वसनीय सेटलमेंट से आता है। एक सामान्य ZSET केवल मेंबर लेक्सिकोग्राफ़िकल ऑर्डर द्वारा टाई करता है। एक निश्चित-चौड़ाई वाला पूर्णांक स्कोर और इनवर्टेड मेंबर एन्कोडिंग काम कर सकता है जब इसकी सटीकता और क्रम सिद्ध हो, लेकिन यह नाजुक है। बड़े पैमाने पर मैं समान मिलीसेकंड, पुनः प्रयास और सही उपलब्धि समय के परीक्षणों के साथ, कंपोजिट कीज़ और ऑर्डर सांख्यिकी का समर्थन करने वाले ऑर्डर्ड शार्ड्स का पक्ष लूँगा।
फॉलो-अप 4: क्या होगा यदि रिवॉर्ड जारी होने के बाद एंटी-चीट चैंपियन को रद्द कर दे?
तकनीकी प्रणाली यह तय नहीं कर सकती कि रिवॉर्ड वापस लिए जाएं या नहीं। यह एक उच्च score_version के साथ एक नकारात्मक सुधार स्वीकार करता है, मूल इवेंट, सबूत, अनुमोदनकर्ता और समय को बरकरार रखता है, और मूल FINAL स्नैपशॉट को फिर से लिखे बिना एक नया सही मैनिफ़ेस्ट तैयार करता है। रिवॉर्ड सेवा रिवॉर्ड को फ्रीज करने, पुनर्प्राप्त करने या बढ़ावा देने के लिए परिचालन नीति लागू करती है और अपने कार्य को लीडरबोर्ड वर्ज़न के साथ जोड़ती है। यह मूल पुरस्कार और बाद के सुधार दोनों की व्याख्या को सुरक्षित रखता है।
फॉलो-अप 5: आप कैसे साबित करते हैं कि लेज़र रीबिल्ड ऑनलाइन लीडरबोर्ड से मेल खाता है?
एक पृथक नेमस्पेस में उसी इनपुट हाई वॉटरमार्क पर इवेंट वर्ज़न्स को रीप्ले करें। प्लेयर काउंट, स्कोर बकेट द्वारा काउंट्स, स्कोर सम और हैश, Top 100, 1 + count(higher score) का उपयोग करके कई स्तरीकृत सैंपल वाले खिलाड़ियों, और प्रत्येक शार्ड के लिए नियतात्मक चेकसम की तुलना करें। दोनों रीड पाथ को शैडो करें और किसी भी स्कोर, रैंक या नेबर-विंडो अंतर को गिनें। प्रत्येक थ्रेशोल्ड पास होने के बाद ही मैनिफ़ेस्ट को स्वैप करें। रीबिल्ड जॉब का सफलतापूर्वक समाप्त होना इसका प्रमाण नहीं है कि उसका परिणाम सही है।