क्विटिंग के लिए लाखों वस्तुओं का भंडारण करने की कुशल विधियाँ, प्रति सेकंड आवेषण की उच्च संख्या के साथ?


15

यह मूल रूप से एक लॉगिंग / काउंटिंग एप्लिकेशन है जो पैकेटों की संख्या की गिनती कर रहा है और एक p2p चैट नेटवर्क पर पैकेट आदि की गिनती कर रहा है। यह 5 मिनट की अवधि में लगभग 4-6 मिलियन पैकेट के बराबर है। और क्योंकि मैं केवल इस जानकारी का "स्नैपशॉट" लेता हूं, मैं केवल हर पांच मिनट में 5 मिनट से पुराने पैकेट निकाल रहा हूं। इसलिए इस संग्रह में आने वाली वस्तुओं की अधिकतम संख्या 10 से 12 मिलियन है।

क्योंकि मुझे अलग-अलग सुपरपेक्टरों के लिए 300 कनेक्शन बनाने की आवश्यकता है, यह संभावना है कि प्रत्येक पैकेट को कम से कम 300 बार डालने की कोशिश की जा रही है (जो शायद इस डेटा को स्मृति में रखना एकमात्र उचित विकल्प है)।

वर्तमान में, मैं इस जानकारी को संग्रहीत करने के लिए एक शब्दकोश का उपयोग कर रहा हूं। लेकिन बड़ी मात्रा में आइटम मैं स्टोर करने की कोशिश कर रहा हूं, इसलिए मैं बड़े ऑब्जेक्ट हीप के साथ मुद्दों में चलता हूं और मेमोरी उपयोग की मात्रा लगातार समय के साथ बढ़ती जाती है।

Dictionary<ulong, Packet>

public class Packet
{
    public ushort RequesterPort;
    public bool IsSearch;
    public string SearchText;
    public bool Flagged;
    public byte PacketType;
    public DateTime TimeStamp;
}

मैंने mysql का उपयोग करने की कोशिश की है, लेकिन यह उन डेटा की मात्रा के साथ रखने में सक्षम नहीं था, जिन्हें मुझे सम्मिलित करने की आवश्यकता है (यह सुनिश्चित करने के लिए कि यह एक डुप्लिकेट नहीं था, जबकि जाँच) और लेनदेन का उपयोग करते समय।

मैंने मोंगोडब की कोशिश की, लेकिन उस के लिए सीपीयू उपयोग पागल था और या तो नहीं रखा।

मेरा मुख्य मुद्दा हर 5 मिनट में उठता है, क्योंकि मैं सभी पैकेटों को हटाता हूं जो 5 मिनट से पुराने हैं, और इस डेटा का "स्नैपशॉट" लेते हैं। जैसा कि मैं एक निश्चित पैकेट प्रकार वाले पैकेट की संख्या की गणना करने के लिए LINQ प्रश्नों का उपयोग कर रहा हूं। मैं डेटा पर एक विशिष्ट () क्वेरी भी कह रहा हूं, जहां मैं keyvaluepair की कुंजी से 4 बाइट्स (आईपी पता) को हटाता हूं, और कुंजीवूपेयर के मूल्य में रिक्वेस्टिंगपोर्ट वैल्यू के साथ संयोजन करता हूं और एक अलग संख्या प्राप्त करने के लिए उपयोग करता हूं। सभी पैकेट से साथियों।

वर्तमान में यह एप्लिकेशन लगभग 1.1GB मेमोरी उपयोग के लिए उपलब्ध है, और जब स्नैपशॉट कहा जाता है तो यह उपयोग को दोगुना करने के लिए इतनी दूर जा सकता है।

अब यह एक मुद्दा नहीं होगा अगर मेरे पास राम की एक पागल राशि है, लेकिन मेरे पास जो vm चल रहा है वह इस समय 2GB RAM तक सीमित है।

क्या कोई आसान उपाय है?


इसका बहुत ही स्मृति गहन परिदृश्य और इसके शीर्ष पर आप अनुप्रयोग चलाने के लिए एक vm का उपयोग कर रहे हैं, वाह। वैसे भी, क्या आपने पैकेटों को संग्रहीत करने के लिए मेमेकैकेट का पता लगाया। मूल रूप से आप एक अलग मशीन पर मेम्केड कर सकते हैं और एप्लिकेशन vm पर ही चल सकता है।

जैसा कि आपने MySQL और MongoDB दोनों को पहले ही आज़मा लिया है, ऐसा लगता है कि शायद आपके आवेदन की आवश्यकताएं (यदि आप इसे सही करना चाहते हैं) निर्धारित करें कि आपको बस अधिक हॉर्स पावर की आवश्यकता है। यदि आपका एप्लिकेशन आपके लिए महत्वपूर्ण है, तो सर्वर पर बीफ़ करें। आप अपने "शुद्ध" कोड को फिर से देखना चाह सकते हैं। मुझे यकीन है कि आप इसे संभालने का एक और अधिक अनुकूलित तरीका पा सकते हैं, जो इसे आपके एप्लिकेशन को बेकार नहीं बनाता है।
मैट बेकमैन

4
आपका प्रोफाइलर आपको क्या बताता है?
जोंकोंक

आपको स्थानीय ढेर से तेज कुछ भी नहीं मिलेगा। मेरा सुझाव शुद्धिकरण के बाद कचरा संग्रहण को मैन्युअल रूप से लागू करना होगा।
vartec

@vartec - वास्तव में, लोकप्रिय धारणा के विपरीत, मैन्युअल रूप से कचरा इकट्ठा करने वाले कलेक्टर वास्तव में तत्काल, अच्छी तरह से गारंटी नहीं देते हैं ... कचरा संग्रह। जीसी खुद के जीसी एल्गोरिदम के अनुसार बाद की अवधि के लिए कार्रवाई को स्थगित कर सकता है। हर 5 मिनट में इसे लगाने से तनाव दूर हो सकता है, बजाय इसके कि आप इसे छोड़ दें। बस कह रहा हूं;)
जस

जवाबों:


12

एक शब्दकोश होने और प्रविष्टियों के लिए उस शब्दकोश को खोजने के बजाय जो बहुत पुराने हैं; 10 शब्दकोश हैं। हर 30 सेकंड या तो एक नया "वर्तमान" शब्दकोश बनाएं और सबसे पुराना शब्दकोश छोड़ दें जिसमें कोई खोज न हो।

अगला, जब आप सबसे पुरानी डिक्शनरी को त्याग रहे हैं, तो पुरानी सभी वस्तुओं को बाद में के लिए एक फिलो कतार में रख दें, और नई वस्तुओं को बनाने के लिए "नई" का उपयोग करने के बजाय एक पुरानी वस्तु को फिल् म कतार से बाहर निकालें और पुराने का पुनर्निर्माण करने के लिए एक विधि का उपयोग करें ऑब्जेक्ट (जब तक कि पुरानी वस्तुओं की कतार खाली न हो)। यह बहुत सारे आवंटन और कचरे के ढेर के ऊपरी हिस्से से बच सकता है।


1
समय टुकड़ा द्वारा विभाजन! बस मैं क्या सुझाव देने जा रहा था।
जेम्स एंडरसन

इसके साथ मुद्दा यह है, मुझे उन सभी शब्दकोशों को क्वेरी करना होगा जो पिछले पांच मिनट के भीतर किए गए थे। जैसे कि 300 कनेक्शन हैं, कम से कम एक बार एक ही पैकेट आने वाला है। इसलिए एक ही पैकेट को एक से अधिक बार न संभालने के लिए, मुझे उन्हें कम से कम 5 मिनट की अवधि के लिए रखना चाहिए।
जोश

1
सामान्य संरचनाओं के साथ समस्या यह है कि वे एक विशिष्ट उद्देश्य के लिए अनुकूलित नहीं हैं। शायद आपको अपने पैकेट संरचना में "nextItemForHash" फ़ील्ड और "nextItemForTimeBucket" फ़ील्ड जोड़ना चाहिए और अपनी स्वयं की हैश तालिका को लागू करना चाहिए, और शब्दकोश का उपयोग करना बंद करना चाहिए। इस तरह आप जल्दी से सभी पैकेट पा सकते हैं जो बहुत पुराने हैं और केवल एक बार खोजते हैं जब एक पैकेट डाला जाता है (यानी आपका केक है और इसे भी खाएं)। यह स्मृति प्रबंधन ओवरहेड के लिए भी मदद करेगा (जैसा कि "शब्दकोश" शब्दकोश प्रबंधन के लिए अतिरिक्त डेटा संरचनाओं को आवंटित / मुक्त नहीं करेगा)।
ब्रेंडन

निर्धारित करने के लिए पहले एक है अगर आप कुछ देखा है सबसे तेज़ तरीका @Josh HashSet । समय-कटा हुआ हैश सेट तेजी से होगा और आपको अभी भी पुरानी वस्तुओं को निकालने के लिए खोज करने की आवश्यकता नहीं होगी। यदि आपने इसे पहले नहीं देखा है, तो आप इसे अपने डिक्शनर (y / ies) में स्टोर कर सकते हैं।
बेसिक


3

पहले सोचा था कि मन में स्प्रिंग्स क्यों आप 5 मिनट इंतजार करते हैं। क्या आप स्नैप-शॉट अधिक बार कर सकते हैं और इस तरह 5 मिनट की सीमा पर आपके द्वारा देखे जाने वाले बड़े अधिभार को कम कर सकते हैं?

दूसरे, LINQ संक्षिप्त कोड के लिए बहुत अच्छा है, लेकिन वास्तव में LINQ "नियमित" C # पर वाक्यगत शर्करा है और इसमें कोई गारंटी नहीं है कि यह सबसे इष्टतम कोड उत्पन्न करेगा। एक अभ्यास के रूप में, आप कोशिश कर सकते हैं और फिर से LINQ के साथ हॉट स्पॉट को फिर से लिख सकते हैं, हो सकता है कि आप प्रदर्शन में सुधार न करें लेकिन आपको स्पष्ट विचार होगा कि आप क्या कर रहे हैं और यह प्रोफाइलिंग कार्य को आसान बना देगा।

डेटा संरचनाओं को देखने के लिए एक और चीज है। मुझे नहीं पता कि आप अपने डेटा के साथ क्या करते हैं, लेकिन क्या आप किसी भी तरह से स्टोर किए गए डेटा को सरल बना सकते हैं? क्या आप एक स्ट्रिंग या बाइट सरणी का उपयोग कर सकते हैं और फिर उन सामानों से संबंधित भागों को निकाल सकते हैं जैसे आपको उनकी आवश्यकता है? क्या आप कक्षा के बजाय एक संरचना का उपयोग कर सकते हैं और यहां तक ​​कि मेमोरी को अलग करने और जीसी रनों से बचने के लिए स्टैकलॉक के साथ कुछ बुराई भी कर सकते हैं?


1
एक स्ट्रिंग / बाइट सरणी का उपयोग न करें, एक बिटआयर की तरह कुछ का उपयोग करें: मैन्युअल रूप से बिट- ट्वेल्ड होने से बचने के लिए msdn.microsoft.com/en-us/library/… । अन्यथा, यह एक अच्छा जवाब है, वास्तव में बेहतर एल्गोरिदम, अधिक हार्डवेयर या बेहतर हार्डवेयर के अलावा एक आसान विकल्प नहीं है।
एड जेम्स

1
पांच मिनट की बात इस तथ्य के कारण है कि इन 300 कनेक्शनों को एक ही पैकेट प्राप्त हो सकता है। इसलिए मुझे इस बात का ध्यान रखना होगा कि मैंने पहले से क्या संभाला है, और इस विशेष नेटवर्क पर सभी नोड्स को पूरी तरह से प्रचारित करने के लिए 5 मिनट का समय है।
जोश

3

सरल दृष्टिकोण: मेमेकैक्ड प्रयास करें ।

  • यह इस तरह के कार्यों को चलाने के लिए अनुकूलित है।
  • यह न केवल आपके व्यस्त बॉक्स पर, बल्कि कम व्यस्त बक्से पर अतिरिक्त मेमोरी का पुन: उपयोग कर सकता है।
  • इसमें अंतर्निहित कैश समाप्ति तंत्र है, जो आलसी है इसलिए कोई हिचकी नहीं है।

नकारात्मक पक्ष यह है कि यह मेमोरी-आधारित है और इसमें कोई दृढ़ता नहीं है। यदि कोई उदाहरण नीचे है, तो डेटा चला गया है। यदि आपको दृढ़ता की आवश्यकता है, तो डेटा को स्वयं अनुक्रमित करें।

अधिक जटिल दृष्टिकोण: रेडिस का प्रयास करें ।

  • यह इस तरह के कार्यों को चलाने के लिए अनुकूलित है।
  • इसमें अंतर्निहित कैश समाप्ति तंत्र है
  • यह आसानी से तराजू / धारियाँ बनाता है।
  • इसमें दृढ़ता है।

नकारात्मक पक्ष यह है कि यह थोड़ा अधिक जटिल है।


1
उपलब्ध राम की मात्रा को बढ़ाने के लिए मशीनों में मेम्केड को विभाजित किया जा सकता है। आपके पास फ़ाइल सिस्टम पर डेटा को क्रमबद्ध करने वाला एक दूसरा सर्वर हो सकता है ताकि आप एक मेमेक बॉक्स नीचे चले जाने पर चीजों को न खोएं। Memcache एपीआई किसी भी भाषा का उपयोग करने के लिए बहुत सरल है और किसी भी भाषा से काम करता है जिससे आप अलग-अलग जगहों पर अलग-अलग स्टैक का उपयोग कर सकते हैं।
माइकल शॉप्सन

1

आपके द्वारा बताए गए प्रश्नों के लिए आपको सभी पैकेजों को संग्रहीत करने की आवश्यकता नहीं है। उदाहरण के लिए - पैकेज प्रकार काउंटर:

आपको दो सरणियाँ चाहिए:

int[] packageCounters = new int[NumberOfTotalTypes];
int[,] counterDifferencePerMinute = new int[6, NumberOfTotalTypes];

पहला सरणी विभिन्न प्रकारों में कितने पैकेजों का ट्रैक रखता है। दूसरा एरे प्रत्येक मिनट में कितने और पैकेज जोड़े गए, इस बात का पता लगाता है कि आप जानते हैं कि हर मिनट के अंतराल पर कितने पैकेज निकालने की जरूरत है। मुझे आशा है कि आप बता सकते हैं कि दूसरी सरणी का उपयोग गोल FIFO कतार के रूप में किया जाता है।

इसलिए प्रत्येक पैकेज के लिए, निम्नलिखित ऑपरेशन किए जाते हैं:

packageCounters[packageType] += 1;
counterDifferencePerMinute[current, packageType] += 1;
if (oneMinutePassed) {
  current = (current + 1) % 6;
  for (int i = 0; i < NumberOfTotalTypes; i++) {
    packageCounters[i] -= counterDifferencePerMinute[current, i];
    counterDifferencePerMinute[current, i] = 0;
}

किसी भी समय, पैकेज काउंटरों को सूचकांक द्वारा तुरंत प्राप्त किया जा सकता है और हम सभी पैकेजों को संग्रहीत नहीं करते हैं।


मेरे द्वारा किए गए डेटा को संग्रहीत करने का मुख्य कारण, यह तथ्य है कि इन 300 कनेक्शनों को एक ही सटीक पैकेट प्राप्त हो सकता है। इसलिए मुझे यह सुनिश्चित करने के लिए कम से कम पांच मिनट के लिए हर देखा गया पैकेट रखना होगा कि मैं उन्हें एक से अधिक बार संभाल / गिन नहीं पा रहा हूं। शब्दकोश कुंजी के लिए ulong कौन सा है।
जोश

1

(मुझे पता है कि यह एक पुराना सवाल है, लेकिन मैं इसी तरह की समस्या के समाधान की तलाश में भर में चला गया था, जहां दूसरा जीन कचरा संग्रह पास कई सेकंड के लिए ऐप को रोक रहा था, इसलिए समान स्थिति में अन्य लोगों के लिए रिकॉर्डिंग करना)।

अपने डेटा के लिए एक वर्ग के बजाय एक संरचना का उपयोग करें (लेकिन याद रखें कि इसे पास-बाय-कॉपी शब्दार्थ के साथ एक मूल्य के रूप में माना जाता है)। यह प्रत्येक खोज पास को करने के लिए gc की खोज का एक स्तर निकालता है।

सरणियों का उपयोग करें (यदि आप जानते हैं कि आपके द्वारा संग्रहीत डेटा का आकार है) या सूची - जो आंतरिक रूप से सरणियों का उपयोग करता है। यदि आपको वास्तव में तीव्र यादृच्छिक अभिगम की आवश्यकता है, तो सरणी सूचकांकों के शब्दकोश का उपयोग करें। यह खोज करने के लिए gc के लिए दूसरे स्तर (या एक दर्जन या अधिक) का उपयोग करता है।

आप जो कर रहे हैं, उसके आधार पर, संरचनाओं की एक सूची खोजना डिक्शनरी लुकअप (मेमोरी के स्थानीयकरण के कारण) से तेज हो सकता है - आपके विशेष एप्लिकेशन के लिए प्रोफ़ाइल।

संरचना और सूची का संयोजन स्मृति उपयोग और कचरा संग्रहकर्ता के आकार दोनों को काफी कम कर देता है।


मेरे पास एक हालिया प्रयोग है, जो डिस्क में संग्रह और शब्दकोशों को तेजी से उत्पन्न करता है, sqlite github.com/modma/PersistenceCollections
ModMa
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.