सवाल मूल्यों को शून्य होने पर कैशिंग से कैसे बचें?


मैं गर्म डेटा को कैश करने के लिए अमरूद का उपयोग कर रहा हूं। जब कैश में डेटा मौजूद नहीं है, तो मुझे इसे इस तरह डेटाबेस से प्राप्त करना होगा।

public final static LoadingCache<ObjectId, User> UID2UCache = CacheBuilder.newBuilder()
        //.maximumSize(2000)
        .weakKeys()
        .weakValues()
        .expireAfterAccess(10, TimeUnit.MINUTES)
        .build(
        new CacheLoader<ObjectId, User>() {
            @Override
            public User load(ObjectId k) throws Exception {
                User u = DataLoader.datastore.find(User.class).field("_id").equal(k).get();
                return u;
            }
        });

मेरी समस्या तब होती है जब डेटा डेटाबेस में मौजूद नहीं होता है, मैं नल वापस लौटना चाहता हूं और कोई कैशिंग नहीं करता हूं। लेकिन अमरूद सिर्फ कैश में कुंजी के साथ शून्य को बचाता है और जब मैं इसे प्राप्त करता हूं तो अपवाद फेंक देता है

com.google.common.cache.CacheLoader $ InvalidCacheLoadException:   कुंजी shisoft के लिए कैशलोडर शून्य वापस आ गया।

तो, कैशिंग शून्य मूल्यों से कैसे बचें?


44
2017-11-14 12:45


मूल


ध्यान दें कि नल को कैशिंग करना आपके एक्सेस पैटर्न के आधार पर आपको बहुत से डेटाबेस एक्सेसों को बचा सकता है। इस प्रकार मैं कुछ विचारों के बिना उन्हें कैश करने से इंकार नहीं करूँगा। - maaartinus
बहुत सच @ मार्टिनस! - Renaud
अमरूद कैश में कुंजी के साथ शून्य को नहीं बचाएगा लेकिन अपवाद फेंक देगा - bylijinnan
एक दिलचस्प बात यह है कि: यदि आप इसके लिए रनटाइम एक्सपेप्शन का उपयोग करते हैं, तो अमरूद इन्हें com.google.common.util.concurrent.AncheckedExecutionException में दोहराता है; - | - Andrew Norman


जवाब:


अगर उपयोगकर्ता नहीं मिला है और इसे इस्तेमाल करते समय क्लाइंट कोड में पकड़ लेता है तो बस कुछ अपवाद फेंक दें get(key) तरीका।

new CacheLoader<ObjectId, User>() {
    @Override
    public User load(ObjectId k) throws Exception {
        User u = DataLoader.datastore.find(User.class).field("_id").equal(k).get();
        if (u != null) {
             return u;
        } else {
             throw new UserNotFoundException();
        }
    }
}

से CacheLoader.load(K) जावाडोक:

Returns:  
  the value associated with key; must not be null  
Throws:  
  Exception - if unable to load the result

शून्य मूल्यों को कैशिंग के बारे में आपके संदेहों का उत्तर देना:

इस कैश में कुंजी से जुड़े मान को वापस लौटाता है, पहले लोड हो रहा है   यदि आवश्यक हो तो वह मूल्य। इसके साथ जुड़े कोई अवलोकन योग्य राज्य नहीं   पूरा होने तक कैश संशोधित किया जाता है

(से LoadingCache.get(K) जावाडोक)

यदि आप अपवाद फेंकते हैं, तो भार को पूर्ण नहीं माना जाता है, इसलिए कोई नया मान कैश नहीं किया जाता है।

संपादित करें:

ध्यान दें कि अंदर कैफीन, जो कि गुवा कैश 2.0 है और "Google Guava प्रेरित API का उपयोग करके एक इन-मेमोरी कैश प्रदान करता है" कर सकते हैं वापसी null से load तरीका:

 Returns:
   the value associated with key or null if not found

यदि आप माइग्रेट करने पर विचार कर सकते हैं, तो उपयोगकर्ता डेटा नहीं मिलने पर आपका डेटा लोडर स्वतंत्र रूप से वापस आ सकता है।


64
2017-11-14 13:42



इस समस्या को हल किया, बहुत धन्यवाद। - Shisoft
Get () विधि केवल सभी चेक अपवाद के लिए ExecutionException फेंकता है। तो क्लाइंट इस उदाहरण में त्रुटि और UserNotFoundException के कारण फेंक दिए गए वास्तविक अपवादों से कैसे भिन्न हो सकता है? आम तौर पर एक अच्छी तरह से डिजाइन एपीआई बोलने से अपने ग्राहकों को सामान्य वर्कफ़्लो को नियंत्रित करने के लिए अपवाद का उपयोग करने के लिए मजबूर नहीं होना चाहिए। - Arash
@ आराश आप पकड़ सकते हैं ExecutionException और इसका कारण होगा UserNotFoundException उस मामले में (बेशक आपको एक करना चाहिए instanceof बाद में जांचें .getCause() यह सुनिश्चित करने के लिए कि यह एक और चेक अपवाद नहीं था)। या आप उपयोग कर सकते हैं getUnckecked (यदि आप चेक अपवादों की कल्पना नहीं करते हैं) या getIfPresent अगर get आपको वह चाहिए जो आपको चाहिए - Xaerxess
यह प्रभावी जावा 69 का उल्लंघन करता है: केवल असाधारण स्थितियों के लिए अपवादों का उपयोग करें। - Adam Bliss
@AdamBliss Guava कैश डिजाइन निर्णयों, विशेष रूप से उस के साथ बहस नहीं कर सकता कैफीन, इसके उत्तराधिकारी, लौटने की अनुमति देता है null से "मूल्य-नहीं-मिला" के रूप में CacheLoader#load(K)। मैंने अपना जवाब संपादित किया है और कैफीन कैश का उल्लेख किया है। - Xaerxess


सरल समाधान: उपयोग करें com.google.common.base.Optional<User> के बजाय User मूल्य के रूप में

public final static LoadingCache<ObjectId, Optional<User>> UID2UCache = CacheBuilder.newBuilder()
        ...
        .build(
        new CacheLoader<ObjectId, Optional<User>>() {
            @Override
            public Optional<User> load(ObjectId k) throws Exception {
                return Optional.fromNullable(DataLoader.datastore.find(User.class).field("_id").equal(k).get());
            }
        });

संपादित करें: मुझे लगता है कि @Xaerxess 'उत्तर बेहतर है।


43
2017-11-14 13:23



तकनीकी रूप से यह "शून्य" मानों को कैश करेगा (यानी। Optional.absent() ऑब्जेक्ट्स), इसलिए यह ओपी के रूप में नकली मूल्यों को कैशिंग से नहीं बचाता है, लेकिन लागत बड़ी नहीं है। - Xaerxess
@Xaerxess आप सही हैं। अपना जवाब वोट दें। - 卢声远 Shengyuan Lu
कोई फर्क नहीं पड़ता। मुझे थोड़ा अलग समस्या थी, और आपके समाधान में मदद मिली। ;) - Haroldo_OK
उपयोग करते समय cache.get( key ), यदि Optional.absent वापस आ गया है, आप बस कर सकते हैं cache.invalidate( key )  सुनिश्चित करना Optional.absent केवल जीवन के लिए है get कहते हैं। और फिर poof .. यह चला गया है। - tolitius
वैकल्पिक .absent () को संग्रहीत करना उन मामलों के लिए बहुत उपयोगी है जहां आप अनुपस्थित मानों को फिर से लोड करने से बचना चाहते हैं, बल्कि अन्य कैश किए गए मानों के समान समाप्ति समय सीमा के साथ, जो वैकल्पिक रूप से अच्छी तरह से काम करता है! - centic


उसी मुद्दे का सामना करना पड़ा, क्योंकि स्रोत में गुम मूल्य सामान्य वर्कफ़्लो का हिस्सा था। अपने आप का उपयोग कर कुछ कोड लिखने से बेहतर कुछ नहीं मिला है getIfPresent, get तथा put तरीकों। नीचे दी गई विधि देखें, जहां local है Cache<Object, Object>:

private <K, V> V getFromLocalCache(K key, Supplier<V> fallback) {
    @SuppressWarnings("unchecked")
    V s = (V) local.getIfPresent(key);
    if (s != null) {
        return s;
    } else {
        V value = fallback.get();
        if (value != null) {
            local.put(key, value);
        }
        return value;
    }
}

2
2018-01-04 06:58





जब आप कुछ नल मूल्यों को कैश करना चाहते हैं, तो आप अन्य कर्मचारियों का उपयोग कर सकते हैं जो अर्थात् नल के रूप में व्यवहार करते हैं।

और समाधान देने से पहले, मैं आपको सलाह देता हूं कि आप लोडिंग कैश को बाहर न खोलें। इसके बजाय, आपको कैश के दायरे को सीमित करने के लिए विधि का उपयोग करना चाहिए।

उदाहरण के लिए, आप उपयोग कर सकते हैं LoadingCache<ObjectId, List<User>> रिटर्न प्रकार के रूप में। और फिर, जब आप डेटाबेस से मूल्य पुनर्प्राप्त नहीं कर सकते हैं तो आप खाली सूची वापस कर सकते हैं। आप -1 का उपयोग इंटीजर या लांग के रूप में कर सकते हैं शून्य मान, आप स्ट्रिंग के रूप में प्रयोग कर सकते हैं शून्य मूल्य, और इतने पर। इसके बाद, आपको नल मूल्य को संभालने के लिए एक विधि प्रदान करनी चाहिए।

when(value equals NULL(-1|"")){
   return null;
}

0
2017-08-08 10:22