2013-01-23 13 views
7

कुशल में कई बार कैश में मौजूद मान को लोड करने से रोकने के लिए कैसे?गैर-कैश किए गए मान को कई बार लोड करने से कैसे रोकें?

एक ठेठ कैश उपयोग निम्नलिखित स्यूडोकोड है:

Object get(Object key) { 
Object value = cache.get(key); 
if (value == null) { 
    value = loadFromService(key); 
    cache.set(key,value); 
} 
return value; 
} 

समस्या: से पहले मूल्य सेवा (डाटाबेस, WebService, RemoteEJB या कुछ और) से भरी हुई है दूसरी कॉल एक ही समय में किया जा सकता , जो एक बार फिर मूल्य लोड हो जाएगा।

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

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

या कोई और तरीका है जिसका मैं उपयोग कर सकता हूं? यदि हां, तो सबसे कुशल क्या होगा?

+1

आप इसे गंभीरता से ओवरइंक कर रहे हैं। जब तक सेवा से डेटा लोड करने का समय भयभीत रूप से लंबा नहीं होता है, यह कभी भी एक समस्या नहीं होगी। – pablochan

+0

मेरे पास कुछ विदेशी ईजेबी कोड है जो परीक्षण वातावरण में 20 सेकंड तक ले सकता है, इसलिए मुझे डर है कि 10 या 20 समवर्ती अनुरोधों के साथ क्या होगा –

उत्तर

3

पहिया बदलने मत करो, का उपयोग करें।

यदि आप एहचेचे का उपयोग कर रहे हैं, तो read-through पढ़ें, यह वह पैटर्न है जिसे आप पूछ रहे हैं। कैश को कैश मिस पर ऑब्जेक्ट्स को पढ़ने के लिए निर्देश देने के लिए आपको CacheEntryFactory इंटरफ़ेस को कार्यान्वित करना होगा, और SelfPopulatingCache के उदाहरण के साथ आपको Ehcache इंस्टेंस को लपेटना होगा।

+0

जैसा कि मैं समझता हूं, कैशलोडर जो मैं अपेक्षा करता हूं वह कर रहा है, सिंक्रनाइज़ेशन के आंतरिक प्रबंधन की आवश्यकता होगी ? –

+0

हां, और बहुत अधिक प्रदान करता है - बेदखल, हटाने श्रोताओं, आदि – mindas

+0

मैं देखता हूं, दिलचस्प, मैं ehcache का उपयोग कर रहा था लेकिन मैं अमरूद का उपयोग करने पर विचार कर सकता हूं, हालांकि डिस्क पर बहने का समर्थन करता है, और फिर भी, इसे कैसे कार्यान्वित करना है दिलचस्प। –

7

कुछ सामान्य रूप से आप ऑब्जेक्ट के हैशकोड का उपयोग करना चाहते हैं।

आपके पास टकराव की संभावना को कम करने के लिए हैशकोड के आधार पर उपयोग किए जाने वाले ताले की एक श्रृंखला हो सकती है। या एक हैक के रूप में आप इस तथ्य का उपयोग कर सकते हैं कि ऑटो-बॉक्स किए गए बाइट हमेशा एक ही ऑब्जेक्ट लौटते हैं। अमरूद के LoadingCache या memoizing supplier

Object get(Object key) { 
    Object value = cache.get(key); 
    if (value == null) { 
     // every possible Byte is cached by the JLS. 
     Byte b = Byte.valueOf((byte) key.hashCode()); 
     synchronized (b) { 
      value = cache.get(key); 
      if (value == null) { 
       value = loadFromService(key); 
       cache.set(key, value); 
      } 
     } 
    } 
    return value; 
} 
+0

हैशकोड के आधार पर लॉक पूलिंग के साथ बढ़िया विचार! लेकिन लॉक होने के बाद आप अन्य प्रक्रिया द्वारा कैश किए गए अपने मूल्य को पा सकते हैं, इसलिए आपको यह भी जांचना चाहिए कि यह लोड हो गया है :) –

+2

वाह, मैं इस तरह से बाइट्स का उपयोग करने के बावजूद कभी नहीं! –

+0

और भी, यह पहली बार है जब मैं मूल्य से पूलिंग बाइट मूल्यों का व्यावहारिक उपयोग देखता हूं :) –

1

लोड होने के समय, परिणाम के बजाय नक्शे में एक मध्यवर्ती वस्तु डालें ताकि यह इंगित किया जा सके कि लोडिंग शुरू हो गई है लेकिन समाप्त नहीं हुई है। Java.util.concurrent.FutureTask के नीचे इंटरमीडिएट ऑब्जेक्ट के लिए उपयोग किया जाता है:

Object get(final Object key) throws Exception { 
    boolean doRun = false; 
    Object value; 
    synchronized (cache) { 
     value = cache.get(key); 
     if (value == null) { 
      value = new FutureTask(new Callable() { 
       @Override 
       public Object call() throws Exception { 
        Object loadedValue = loadFromService(key); 
        synchronized (cache) {cache.put(key, loadedValue);}; 
        return loadedValue; 
       } 

      }); 
      cache.put(key, value); 
      doRun=true; 
     } 
    } 
    if (value instanceof FutureTask) { 
     FutureTask task = (FutureTask) value; 
     if (doRun) { 
      task.run(); 
     } 
     return task.get(); 
    } 
    return value; 
}` 
+0

हम्म आपके समाधान को पूरे कैश पर हमेशा सिंक्रनाइज़ेशन करने की आवश्यकता होती है, लेकिन सिंक्रनाइज़ किया गया हिस्सा काफी तेज़ है। आरंभ में प्राप्त करने के बारे में आप क्या सोचेंगे, और सिंक्रनाइज़ किए गए भाग को केवल तभी चलाया जा सकता है जब मान शून्य हो? –

+0

यह इस बात पर निर्भर करता है कि प्रति सेकंड कैश के कितने अनुरोध हैं। सिंक्रनाइज़ किया गया हिस्सा 1 माइक्रोसॉन्ड से कम लंबा होता है, इसलिए यदि आपकी दर प्रति सेकंड 100000 से कम अनुरोध है, तो टकराव का मौका वैसे भी नगण्य है, इसलिए कोई भी जटिलता कोई प्रभाव नहीं देगी। यदि दर अधिक है, तो एक और कहानी है, और आपको प्रोसेसर कैशिंग, थ्रेड स्विचिंग और कचरा कलेक्टर समेत कई अलग-अलग चीजों को ध्यान में रखना होगा, जिनमें से आपके कैश तक पहुंच प्रदर्शन बिंदु से पहले स्थान पर नहीं हो सकती है मानना ​​है कि। –

संबंधित मुद्दे