VPN पर Redis धीमा लगता है: प्रति अनुरोध राउंड ट्रिप कम करना
VPN दूरस्थ Redis कॉल को स्थानीय नहीं बनाता। प्रति अनुरोध राउंड ट्रिप का बजट बनाएँ, कनेक्शन दोबारा इस्तेमाल करें और पाइपलाइन को लेनदेन से दूर रखें।
स्थानीय Redis के सामने 200 ms में जवाब देने वाला पेज क्लाइंट के VPN के पीछे बैठते ही चार सेकंड लेता है, जबकि सर्वर के काउंटर खाली पड़े रहते हैं। धीमा लॉग एक मिलीसेकंड से ऊपर कुछ नहीं दिखाता, CPU का ग्राफ सपाट है, और इंस्टेंस बिना शिकायत दूसरे क्लाइंट की सेवा करता है। जो बदला है वह दूरी है, और दूरी को कई गुना करने वाली चीज़ है आपका कोड जितनी बार रुककर इंतज़ार करता है। अनुरोध का रास्ता क्लाइंट से बाहर की ओर चलिए और हल लगभग हमेशा तेज़ सर्वर नहीं, बल्कि कम क्रमिक इंतज़ार होता है।
सर्वर के मीट्रिक से नहीं, क्लाइंट से शुरू करें
पहले आदेश और आखिरी आदेश के बीच क्लाइंट प्रक्रिया को देखें। अगर ट्रेस जवाब, विराम, फिर अगला आदेश दिखाता है, तो आप हर आदेश पर पूरा राउंड ट्रिप चुका रहे हैं और सर्वर ट्यूनिंग की कोई मात्रा इसे नहीं बदलेगी। धीमा लॉग यहाँ गलत औज़ार है, क्योंकि वह निष्पादन समय दर्ज करता है और संचार I/O को जानबूझकर बाहर रखता है। धीमे एंडपॉइंट के बगल में साफ़ धीमा लॉग राउंड-ट्रिप समस्या का हस्ताक्षर है।
कोड छूने से पहले रास्ता ही मापें। redis-cli -h redis.example.internal --latency बार-बार PING भेजता है और औसत अनुरोध और जवाब समय छापता है, जो आपका प्रति-अदला-बदली खर्च है। इसे एक उपयोगकर्ता क्रिया में क्रमिक आदेशों की संख्या से गुणा करें और आपको एंडपॉइंट के लिए न्यूनतम सीमा मिल जाएगी।
एक उपयोगकर्ता क्रिया को वास्तव में कितने राउंड ट्रिप चाहिए, गिनें
एक एंडपॉइंट लें और हर Redis कॉल क्रम में लिखें, उन सहित जो किसी ORM, सत्र मिडलवेयर या फ़ीचर-फ़्लैग खोज के अंदर छिपी हैं। अधिकतर धीमे एंडपॉइंट वहाँ बीस या तीस कॉल करते पाए जाते हैं जहाँ तीन काफ़ी होतीं, और छिपी हुई कॉल आमतौर पर बहुमत में होती हैं।
निर्भरता की श्रृंखला को समेटा नहीं जा सकता: अगर दूसरे आदेश को पहले के लौटाए गए कुंजी की ज़रूरत है, तो इंतज़ार असली है और एकमात्र विकल्प अलग डेटा मॉडल या सर्वर-पक्ष का ऐसा संचालन है जो पूरा चरण एक साथ व्यक्त करे। स्वतंत्र पढ़ाई अलग मामला है, क्योंकि दूसरा भेजने से पहले पहले जवाब का इंतज़ार करने को कुछ बाध्य नहीं करता। कुछ भी संपादित करने से पहले हर कॉल को निर्भर या स्वतंत्र के रूप में चिह्नित करें, और हल का स्वरूप स्पष्ट हो जाएगा।
मायने रखने वाली संख्या क्रमिक इंतज़ार है, कुल आदेश नहीं। एक बैच में तीस आदेश फिर भी एक इंतज़ार का खर्च करते हैं।
सिर्फ़ वही आदेश बैच करें जो एक-दूसरे पर निर्भर नहीं हैं
पाइपलाइन स्वतंत्र आदेशों को कतार में लगाती है और जवाब क्रम से इकट्ठे करती है, N इंतज़ारों को एक में बदलती है। अधिकतर क्लाइंट इसे अलग कनेक्शन मोड के बजाय ब्लॉक, कॉलबैक या संदर्भ प्रबंधक के रूप में देते हैं, इसलिए अपने स्थापित संस्करण की API जाँचें। लाभ राउंड-ट्रिप समय के साथ बढ़ता है, और इसी कारण VPN पर यह मायने रखता है और localhost पर लगभग महसूस नहीं होता।
import redis
r = redis.Redis(host="redis.example.internal", port=6379, socket_timeout=2)
pipe = r.pipeline(transaction=False)
for key in ("user:1001", "user:1002", "user:1003"):
pipe.get(key)
print(pipe.execute())
जब सिर्फ़ बैचिंग चाहिए तो transaction=False दें। डिफ़ॉल्ट के साथ क्लाइंट बैच को MULTI और EXEC में लपेटता है, जो इसे परमाणु बनाता है और बदल देता है कि दूसरे क्लाइंट बीच में क्या देख सकते हैं। परमाणुता शुद्धता का फ़ैसला है, प्रदर्शन का बटन नहीं, और पाइपलाइन सिर्फ़ इसलिए लेनदेन नहीं है कि दोनों राउंड ट्रिप घटाते हैं।
सिर्फ़ बैच जमा करने के बजाय हर जवाब पढ़ें। कतार में लगा आदेश त्रुटि लौटा सकता है जबकि बैच पूरी तरह पूरा हो जाता है, और जवाबों की सूची फेंकने वाला क्लाइंट विफल लेखन को चुपचाप लेखन में बदल देता है। जवाबों की सूची की लंबाई की तुलना आपके द्वारा कतार में लगाए गए आदेशों की संख्या से करें।
क्रियाओं के बीच कनेक्शन जीवित रखें
हर नया कनेक्शन नाम-विश्लेषण, TCP हैंडशेक, TLS हैंडशेक और AUTH का खर्च उठाता है, और VPN पर इनमें से हर एक ऐसा राउंड ट्रिप है जिसे आप बैच करके हटा नहीं सकते। क्लाइंट को प्रति प्रक्रिया एक बार बनाएँ और दोबारा इस्तेमाल करें, या अपनी लाइब्रेरी द्वारा दिए गए सीमित आकार के पूल का उपयोग करें। हर अनुरोध पर दोबारा कनेक्ट होना प्रति-आदेश इंतज़ार के बाद इस लक्षण का दूसरा सबसे आम कारण है।
कॉन्फ़िगरेशन में शाब्दिक पते के बजाय निजी होस्टनाम रखें। निजी नेटवर्क पर भी प्रमाणपत्र सत्यापन और सेवा पहचान लागू रहते हैं, और IP पिन करना उसी पल टूट जाता है जब प्रदाता किसी सबनेट का पुनःक्रमांकन करता है। अगर नाम-विश्लेषण धीमा हिस्सा है, तो कनेक्शन स्ट्रिंग नहीं, रिज़ॉल्वर पथ ठीक करें।
स्पष्ट सॉकेट टाइमआउट सेट करें ताकि अटका हुआ टनल किसी वर्कर को लटकाने के बजाय जल्दी विफल हो। फिर क्लाइंट में कनेक्शन निर्माण का लॉग रखकर पुष्टि करें कि पूल वास्तव में दोबारा इस्तेमाल हो रहा है।
मेमोरी समस्या बनने से पहले बैच का आकार तय करें
Redis बैच की पूरी अवधि तक कतारबद्ध जवाब रखता है, और क्लाइंट बाद में पार्स किया गया परिणाम रखता है। दस हज़ार बड़े मानों की पाइपलाइन नेटवर्क इंतज़ारों को मेमोरी के उछाल और दोनों सिरों पर लंबे विराम से बदल देती है।
बैच का आकार आदेशों की संख्या नहीं, जवाब की मात्रा के हिसाब से चुनें। एक बैच में कुछ सौ छोटे आदेश आमतौर पर आरामदायक होते हैं, जबकि सौ मल्टी-किलोबाइट पेलोड नहीं। बाइट और संख्या दोनों से विभाजित करें, और हर बैच को एंडपॉइंट के वास्तविक लेटेंसी बजट के भीतर रखें।
बदलाव के बाद शुरू से अंत तक मापें: redis-cli -h redis.example.internal --latency-history दिखाता है कि रास्ता ख़ुद स्थिर है या नहीं, और आपका अपना अनुरोध टाइमर दिखाता है कि एंडपॉइंट सुधरा या नहीं। अगर लेटेंसी इतिहास सपाट होते हुए भी एंडपॉइंट धीमा रहे, तो बचा हुआ खर्च सर्वर-पक्ष का काम या आपका अपना सीरियलाइज़ेशन है, और अगला क़दम बैचिंग में एक और बदलाव नहीं, हैंडलर की प्रोफ़ाइल है।