टीएल;डीआर
- समस्या: बड़े पैमाने की वास्तुकला (आर्किटेक्चर) तैयार करने के लिए उपलब्धता, थ्रूपुट और परिचालन जटिलता के बीच संतुलन बनाना आवश्यक है।
- मुख्य निष्कर्ष: शुरुआती के लिए यूनिक आईडी: एक डेटाबेस काउंटर कई राइटर पर क्यों टूटता है, फिर यूयूआईडी, टिकट सर्वर, और स्नोफ्लेक जैसे समय प्लस मशीन नंबर प्लस काउंटर, रसीद की तरह, साथ में पीछे कूदने वाली घड़ियाँ।
- परिणाम: उत्पादन वातावरण में विफलता से निपटने और प्रदर्शन लक्ष्यों को हासिल करने की सटीक रूपरेखा।
हर ऑर्डर, ट्वीट, फ़ोटो और चैट मैसेज को एक ऐसा नाम चाहिए जो किसी और चीज़ के साथ साझा न हो। वह नाम यूनिक आईडी है। एक लैपटॉप पर यह आसान है: १, फिर २, फिर ३। कंपनी स्केल पर कई मशीनें एक साथ नाम गढ़ती हैं। दो मशीनें कभी एक ही नाम नहीं गढ़ सकतीं।
सोचो बैंक चेक नंबर, बेकरी की टिकट मशीन, और दुकान की रसीद। हर सिस्टम "अगला यूनिक नंबर दो" को अलग तरीके से हल करता है। यह पोस्ट उन्हीं रोजमर्रा की तस्वीरों से कंप्यूटर वाली बात को आसान बनाती है।
हम कौन सी समस्या हल कर रहे हैं?
जब तुम यूज़र या ऑर्डर बनाते हो, सिस्टम एक आईडी देता है। बाद में हर सर्विस "ऑर्डर ९१८२७३६४" ढूँढती है और ठीक एक पंक्ति चाहती है।
अच्छी आईडी में आमतौर पर ये गुण होते हैं:
| ज़रूरत | सादी भाषा में |
|---|---|
| यूनिक | कोई दो चीज़ें एक ही आईडी न पाएँ |
| तेज़ | आईडी बनाना चेकआउट का धीमा हिस्सा न बने |
| अक्सर संख्यात्मक | डेटाबेस के एक पूर्णांक कॉलम में फिट |
| अक्सर लगभग समय-क्रमित | नई आईडी बड़ी; फीड और इंडेक्स साफ़ रहें |
गैप लगभग हमेशा ठीक हैं। हर पूर्णांक नहीं चाहिए। टक्कर शून्य चाहिए।
बाकी पोस्ट में हम चाहते हैं: ६४-बिट संख्याएँ (सामान्य एसक्यूएल BIGINT में फिट), पूरे प्रोडक्ट में यूनिक, और लगभग समय के हिसाब से क्रमबद्ध।
एक डेटाबेस ऑटो-इन्क्रीमेंट कई मशीनों के लिखने पर क्यों टूटता है
एक डेटाबेस पर ऑटो-इन्क्रीमेंट प्राइमरी की वैसी है जैसे एक शाखा में एक चेक बुक। बैंक चेक १, फिर २, फिर ३ पर मुहर लगाता है। सही है, क्योंकि मुहर एक ही है।
बढ़ने पर समस्याएँ:
१. कई राइटर। डेटा कई डेटाबेस में बाँटो। हर एक अपना काउंटर चाहता है। दो काउंटर कहें "अगला ७ है।" टक्कर। २. कई ऐप सर्वर। बीस पॉड पंक्तियाँ डालते हैं। अगर एक डेटाबेस काउंटर का मालिक है, वह काउंटर वह कतार बन जाता है जहाँ सब खड़े हैं। ३. दूर के रीजन। भारत का सर्वर अमेरिका के काउंटर का इंतज़ार करे तो हर क्रिएट पर नेटवर्क देरी। ४. फेलओवर। जो मशीन "अगला नंबर जानती है" बीच स्विच में गिरे, तो दो तरफ़ गलती से एक ही रेंज दोबारा चल सकती है।
हर शार्ड को अपनी सीक्वेंस दे सकते हो (यूज़र १००१ शार्ड ए पर लोकल आईडी के साथ)। कुछ प्रोडक्ट में चलता है। अतिरिक्त काम के बिना शार्ड्स के बीच एक ग्लोबल क्रम नहीं मिलता।
संक्षेप: एक मुहर सुरक्षित है और स्केल पर धीमी पड़ती है। कई मुहरों के लिए स्मार्ट नियम चाहिए।
विकल्प १: यूयूआईडी (रैंडम पासपोर्ट नंबर)
यूयूआईडी लंबा आइडेंटिफ़ायर है, आमतौर पर १२८ बिट। आम स्ट्रिंग कुछ ऐसी:
09c93e62-50b4-468d-bf8a-c07e1040bfb2
रोजमर्रा की तुलना: हर फ़ोन या सर्वर कई पासे फेंककर पासपोर्ट नंबर गढ़ता है। कोई केंद्रीय दफ़्तर कॉल नहीं करता। सामान्य प्रोडक्ट स्केल पर पूरा नंबर मिलने की संभावना बहुत छोटी होती है।
लोग क्यों पसंद करते हैं
- कोई भी मशीन ऑफ़लाइन आईडी बना सकती है: फ़ोन, लैपटॉप, क्लाउड पॉड।
- साझा काउंटर नहीं। एक मशीन को ओवरलोड करने की ज़रूरत नहीं।
- होरिज़ॉन्टल स्केल मुफ़्त।
इंटरव्यू और डेटाबेस अक्सर क्यों हिचकते हैं
- १२८ बिट, ६४ नहीं। सादे पूर्णांक से चौड़ी की और ज़्यादा स्टोरेज।
- रैंडम यूयूआईडी (वर्शन ४) समय के साथ नहीं बढ़ते। इंडेक्स रैंडम क्रम में इन्सर्ट पाता है; पेज धीमे और कैश खराब हो सकते हैं।
- डैश वाली स्ट्रिंग बदसूरत लगती है अगर "सिर्फ नंबर" चाहिए थे।
नया स्टाइल (यूयूआईडीवी७) समय आगे रखता है ताकि आईडी लगभग बनाने के समय से सॉर्ट हों। फिर भी १२८ बिट चुकाने पड़ते हैं।
यूयूआईडी कब जीतता है: क्लाइंट पर बनी आईडी, ऑफ़लाइन ऐप, या १२८-बिट की से खुश टीमें।
विकल्प २: टिकट सर्वर (एक बेकरी टिकट मशीन)
टिकट सर्वर छोटा सिस्टम है जिसका एक ही काम अगला नंबर देना है। फ़्लिकर ने सालों पहले प्रसिद्ध वर्शन बताया: छोटा डेटाबेस ऑटो-इन्क्रीमेंट चलाता है और नया आईडी लौटाता है। हर ऐप पूछती है: "अगला क्या है?"
रोजमर्रा की तुलना: बेकरी की कागज़ी टिकट मशीन। सब एक ही मशीन से टिकट निकालते हैं। नंबर यूनिक रहते हैं। मशीन अटक जाए तो कतार रुक जाती है।
लोग क्यों पसंद करते हैं
- छोटी, सादी संख्यात्मक आईडी।
- समझाना और डीबग आसान।
- छोटे-मध्यम रिट रेट के लिए काफी।
ऊँचे लोड पर क्यों दुखता है
- हर क्रिएट उसी मशीन (या छोटे जोड़े) पर निर्भर। टिकट रुकें तो क्रिएट रुकें।
- दो टिकट मशीनों को बँटवारे का नियम चाहिए (विषम/सम, या अलग रेंज) ताकि टक्कर न हो। फिर कॉन्फ़िग जोखिम लौटता है।
- थ्रूपुट छत लगभग "एक काउंटर कितना धकेल सकता है।"
आम अपग्रेड: टिकटों का ब्लॉक दो। हर ऑर्डर पर एक नंबर माँगने के बजाय हर ऐप सर्वर को रेंज मिलती है, जैसे ५००० से ५९९९। वह लोकल खर्च करता है। टिकट मशीन कम कॉल होती है। कई प्रोडक्शन सिस्टम इसी तरह आईडी बाँटते हैं।
फिर भी रेंज का मालिक कोई केंद्रीय पक्ष ही है। यही ट्रेड-ऑफ़ है।
विकल्प ३: स्नोफ्लेक (रसीद: समय + मशीन + काउंटर)
स्नोफ्लेक (ट्विटर-शैली डिज़ाइन; क्लाउड वेयरहाउस नहीं) ६४-बिट आईडी तीन विचारों से बनाता है:
१. कब बनी (टाइमस्टैम्प)। २. किस मशीन ने बनाई (वर्कर या मशीन आईडी)। ३. उस मशीन पर उस छोटे समय टुकड़े में कौन सी गिनती (सीक्वेंस)।
रोजमर्रा की तुलना: दुकान की रसीद।
- पहले तारीख और समय छपता है।
- फिर काउंटर नंबर (काउंटर ३ बनाम काउंटर ७)।
- उसी पल उसी काउंटर का लोकल क्रम लाइन खत्म करता है।
दो काउंटर एक ही सेकंड में दोनों "आइटम ४" छाप सकते हैं। रसीद फिर भी यूनिक है क्योंकि काउंटर नंबर अलग है। एक ही काउंटर एक ही मिलीसेकंड में दो आइटम छापे तो लोकल काउंटर बढ़ता है।
आम शिक्षण लेआउट:
0 41 bits 5 5 12
+-+----------------------------------------+-----+-----+----------+
|0| timestamp (ms) | DC | Wkr | sequence |
+-+----------------------------------------+-----+-----+----------+
| टुकड़ा | बिट | सादी भाषा में भूमिका |
|---|---|---|
| अनुपयोगी / साइन | १ | संख्या धनात्मक रखें |
| टाइमस्टैम्प | ४१ | चुनी शुरुआत तारीख से मिलीसेकंड (प्रोडक्ट लॉन्च; १९७० ज़रूरी नहीं) |
| डेटासेंटर | ५ | कौन सी बिल्डिंग या रीजन (३२ तक) |
| वर्कर | ५ | उस बिल्डिंग में कौन सी मशीन (३२ तक) |
| सीक्वेंस | १२ | उस मिलीसेकंड में उस वर्कर पर काउंटर (४०९६ तक) |
कुछ टीमें डेटासेंटर और वर्कर को १०-बिट मशीन आईडी में मिलाती हैं। विचार वही: समय + मशीन नंबर + काउंटर।
यह आम लक्ष्यों से क्यों मिलता है
- ६४ बिट में फिट।
- यूनिक अगर वर्कर आईडी यूनिक रहें और उसी मिलीसेकंड में उसी वर्कर पर सीक्वेंस रैप न हो।
- लगभग समय-क्रमित: बड़ा टाइमस्टैम्प बड़ी आईडी (अगर घड़ियाँ ईमानदार हों)।
- ऊँचा थ्रूपुट: सिद्धांत में प्रति मशीन प्रति मिलीसेकंड हज़ारों आईडी। असल सीमा अक्सर सीपीयू और एपीआई सर्व करने का तरीका।
क्षमता के नंबर जो जानने लायक हैं
- ४१ बिट मिलीसेकंड चुनी शुरुआत से लगभग ६९ साल। लॉन्च के पास शुरुआत चुनो ताकि खाली दशक बर्बाद न हों।
- १२ बिट सीक्वेंस = प्रति वर्कर प्रति मिलीसेकंड ४०९६ आईडी। ज़्यादा चाहिए तो अगला मिलीसेकंड ठहरो।
- वर्कर आईडी टकरानी नहीं चाहिए। दो प्रोसेस एक ही वर्कर "७" साझा करें तो एक ही आईडी कट सकती है। वर्कर सावधानी से असाइन करो (कॉन्फ़िग, कोऑर्डिनेटर से लीज़, या स्थिर मैप)।
छोटा एनकोड स्केच (एक प्रोसेस)
import time
import threading
class Snowflake:
def __init__(self, datacenter_id: int, worker_id: int, epoch_ms: int):
assert 0 <= datacenter_id < 32
assert 0 <= worker_id < 32
self.datacenter_id = datacenter_id
self.worker_id = worker_id
self.epoch_ms = epoch_ms
self.sequence = 0
self.last_ms = -1
self.lock = threading.Lock()
def next_id(self) -> int:
with self.lock:
now = int(time.time() * 1000)
if now < self.last_ms:
raise RuntimeError("clock went backwards")
if now == self.last_ms:
self.sequence = (self.sequence + 1) & 0xFFF
if self.sequence == 0:
while now <= self.last_ms:
now = int(time.time() * 1000)
else:
self.sequence = 0
self.last_ms = now
ts = now - self.epoch_ms
return (
(ts << 22)
| (self.datacenter_id << 17)
| (self.worker_id << 12)
| self.sequence
)
नोट्स:
- कस्टम शुरुआत से ४१-बिट फ़ील्ड दिन एक से ज़्यादा चलती है।
- लॉक एक प्रोसेस में सीक्वेंस बचाता है। एक बॉक्स पर दो प्रोसेस को दो वर्कर आईडी चाहिए।
- सीक्वेंस खत्म हो तो ठहरो। उसी मिलीसेकंड में रैप करके नंबर दोबारा मत लो।
घड़ी की समस्याएँ सादी भाषा में
स्नोफ्लेक समय पर भरोसा करता है। असली कंप्यूटरों की घड़ियाँ कभी-कभी गड़बड़ करती हैं।
पीछे कूदने वाली घड़ियाँ
सर्वर एनटीपी से समय मिलाते हैं। कभी घड़ी धीरे सरकती है (अच्छा)। कभी उसे एक झटके में पीछे धकेल दिया जाता है (आईडी जनरेटर के लिए खतरनाक)।
सोचो रसीद प्रिंटर मानता है समय ३:००:१० है, एक बैच छापता है, फिर दीवार की घड़ी जबरन ३:००:०५ हो जाती है। अगर तुम वही काउंटर नंबर और शून्य से शुरू लोकल काउंटर लेकर फिर छापो, पहले इस्तेमाल नंबर फिर छपेंगे। यही टक्कर है।
सुरक्षित आदतें:
१. मिंट बंद रखो जब तक घड़ी आखिरी इस्तेमाल समय से आगे न आए। २. थोड़ा पीछे हो तो कुछ मिलीसेकंड सो जाओ। ३. लॉजिकल आखिरी समय रखो: दीवार-घड़ी थोड़ी पीछे जाए तो आखिरी जाना हुआ समय रखो और सीक्वेंस जलाओ; सीक्वेंस खत्म हो तो ठहरो। ४. मोनोटोनिक घड़ियाँ (एक मशीन पर सिर्फ आगे बढ़ने वाले टाइमर) आंतरिक ताल में मदद करती हैं, पर टाइमस्टैम्प फ़ील्ड को अभी भी साझा दीवार-समय चाहिए ताकि मशीनों के बीच क्रम बने।
जमना और रीस्टार्ट
प्रोसेस रुक सकता है (गार्बेज कलेक्शन, वीएम पॉज़) और बाद में जाग सकता है। रीस्टार्ट पर पुराना (वर्कर, मिलीसेकंड, सीक्वेंस) त्रिक दोबारा मत लो। अगर प्रति वर्कर हाई-वॉटर मार्क रखा हो, तब तक ठहरो जब तक वर्तमान समय उस निशान से आगे न हो।
अलग शहर, अलग घड़ियाँ
आईडी उन्हीं घड़ियों के क्रम में हैं जिन्होंने उन्हें बनाया। रीजन ए रीजन बी से कुछ मिलीसेकंड अलग हो सकता है। बी में पहले हुआ इवेंट तब भी बड़ी आईडी पा सकता है अगर बी की घड़ी आगे हो। सख्त ग्लोबल क्रम के लिए और औज़ार चाहिए। ज़्यादातर प्रोडक्ट के लिए "लगभग क्रम" काफी है। ईमानदारी से कहो।
ऑपरेशनल साफ़-सफ़ाई
- हर आईडी वर्कर पर समय सिंक (क्रोनी या एनटीपीडी) चलाओ।
- बड़े ऑफ़सेट और अचानक स्टेप पर अलर्ट।
- लाइव जनरेटर पर हाथ से घड़ी मत सेट करो।
- कुछ टीमें आईडी सिर्फ अच्छी निगरानी वाली छोटी मशीन सेट पर बनाती हैं।
जनरेटर कहाँ रहे
| जगह | ताकत | कमज़ोरी |
|---|---|---|
| हर सर्विस में लाइब्रेरी | सबसे तेज़, कोई अतिरिक्त हॉप नहीं | हर प्रोसेस को यूनिक वर्कर आईडी |
| लोकल साइडकार | एक इम्प्लीमेंटेशन, फिर भी पास | पॉड जीवनचक्र से जुड़ा |
| केंद्रीय मिंट एपीआई | ऑडिट आसान | नेटवर्क देरी और साझा आउटेज जोखिम |
| केंद्र से रेंज, लोकल खर्च | अक्सर व्यावहारिक बीच | रेंज असुरक्षित रूप से न खोएँ |
ऊँचे रिट रेट पर आमतौर पर सावधानी से असाइन वर्कर आईडी वाला इन-प्रोसेस स्नोफ्लेक। कम रेट वाले एडमिन ऑब्जेक्ट पर टिकट सर्वर या सादा डेटाबेस सीक्वेंस ठीक।
सुरक्षा नोट (छोटे)
- सीक्वेंशियल या समय-सॉर्टेबल आईडी वॉल्यूम और मोटा समय लीक करती हैं। उन्हें सीक्रेट टोकन मत समझो। हमेशा असली ऑथ से ऑथराइज़ करो।
- कभी अंदर स्नोफ्लेक आईडी रखो और यूज़र को अलग रैंडम पब्लिक आईडी दिखाओ।
- टाइमस्टैम्प-भारी आईडी बता सकती हैं कि चीज़ कब बनी। लॉग और यूआरएल उसी हिसाब से।
साथ-साथ तुलना
| तरीका | रोजमर्रा की तस्वीर | बिट | समय क्रम | कोऑर्डिनेशन | मुख्य फेलियर |
|---|---|---|---|---|---|
| डेटाबेस ऑटो-इन्क्रीमेंट | एक चेक बुक | ६४ | एक प्राइमरी पर हाँ | एक डेटाबेस | वही डेटाबेस बॉटलनेक |
| यूयूआईडी वी४ | रैंडम पासपोर्ट | १२८ | नहीं | कोई नहीं | इंडेक्स में रैंडम इन्सर्ट |
| यूयूआईडी वी७ | आगे तारीख वाला पासपोर्ट | १२८ | हाँ | कोई नहीं (लोकल घड़ी) | चौड़ी की |
| टिकट सर्वर | बेकरी टिकट मशीन | ६४ | हाँ | केंद्रीय टिकट | मशीन अटकी, कतार रुकी |
| स्नोफ्लेक | रसीद: समय + काउंटर + क्रम | ६४ | लगभग हाँ | यूनिक वर्कर आईडी | घड़ी कूद, साझा वर्कर आईडी |
दोस्त को सुनाने वाला सार
कल्पना करो हर नए ऑर्डर को यूनिक टिकट नंबर चाहिए।
- एक डेटाबेस काउंटर एक शाखा की एक मुहर है। सुरक्षित जब तक कई शाखाएँ एक साथ मुहर न लगाएँ, या सब एक ही मुहर पर कतार न बनाएँ।
- यूयूआईडी लंबे पासपोर्ट के लिए पासे फेंकना है। कोई केंद्रीय दफ़्तर नहीं। नंबर बड़ा और अक्सर रैंडम; डेटाबेस इंडेक्स बिखर सकते हैं।
- टिकट सर्वर बेकरी की मशीन है। सब एक जगह से निकालते हैं। नंबर साफ़ रहते हैं। मशीन मर जाए तो किसी को नंबर नहीं, जब तक पहले से टिकटों के ब्लॉक न बाँटे हों।
- स्नोफ्लेक दुकान की रसीद है: समय + मशीन नंबर + लोकल काउंटर। मशीनें हर बार घर कॉल किए बिना समानांतर काम करती हैं। हर मशीन को अपना नंबर दो, और पीछे कूदने वाली घड़ी पर बिना सुरक्षा भरोसा मत करो।
अगर सिर्फ दो प्रोडक्शन बग याद रखो: दो प्रोसेस एक ही वर्कर आईडी साझा करें, और समय पीछे जाए जबकि सीक्वेंस रीसेट हो। बिट लेआउट आसान हिस्सा है। पहचान और समय की रक्षा असली काम है।
एक डिज़ाइन जिसे बचा सको (छोटा)
चाहिए: ६४-बिट संख्याएँ, पूरे प्रोडक्ट में यूनिक, लगभग समय-क्रमित, प्रति सेकंड हज़ारों आईडी, मल्टी-एजेड।
प्रस्ताव:
१. स्नोफ्लेक-शैली बिट: अनुपयोगी + टाइमस्टैम्प + वर्कर + सीक्वेंस।
२. कस्टम युग = सर्विस लॉन्च दिन यूटीसी।
३. वर्कर आईडी लीज़ या असाइन ताकि कोई दो जीवित जनरेटर एक न बाँटें।
४. प्रोसेस के अंदर मिंट; हर इन्सर्ट पर रिमोट कॉल नहीं।
५. घड़ी रोलबैक पर: मिंट बंद और अलर्ट।
६. BIGINT में स्टोर। पब्लिक जेएसओएन एपीआई में दशमलव स्ट्रिंग सोचो ताकि जावास्क्रिप्ट क्लाइंट 2^53 - 1 से ऊपर प्रिसिजन न खोएँ।
ज़ोर से कब और चुनें: १२८ बिट ठीक और सरलता जीते तो यूयूआईडी। रिट रेट कम और छोटी पूर्णांक मायने रखें तो टिकट सर्वर। कॉम्पैक्ट, सॉर्टेबल, ऊँचे रेट आईडी चाहिए और वर्कर पहचान व घड़ी अनुशासन में निवेश करोगे तो स्नोफ्लेक।
समापन
यूनिक आईडी एक लाइन की फ़ीचर लगती हैं जब तक कई राइटर एक प्रोडक्ट साझा न करें। यूयूआईडी कोऑर्डिनेशन हटाता है और चौड़ाई तथा (रैंडम वर्शन पर) इंडेक्स लोकलिटी की कीमत लेता है। टिकट सर्वर छोटी पूर्णांक रखते हैं और रेंज न दो तो केंद्रीय चोक लौटाते हैं। स्नोफ्लेक समय, मशीन पहचान और प्रति-टिक काउंटर को ६४ बिट में बाँधता है, जैसे रसीद जो एक ही काउंटर पर एक ही पल एक ही लाइन दोबारा न छापे।
घड़ी और वर्कर आईडी की रक्षा करो। बाकी अंकगणित है।
