कैश अमान्यकरण - क्या कोई सामान्य समाधान है?


118

"कंप्यूटर विज्ञान में केवल दो कठिन समस्याएं हैं: कैश अमान्यकरण और नामकरण की चीजें।"

फिल कार्लटन

क्या कैश को अमान्य करने का एक सामान्य समाधान या तरीका है; यह जानने के लिए कि कोई प्रविष्टि कब बासी है, इसलिए आपको हमेशा ताजा डेटा प्राप्त करने की गारंटी दी जाती है?

उदाहरण के लिए, एक फ़ंक्शन पर विचार करें getData()जो फ़ाइल से डेटा प्राप्त करता है। यह फ़ाइल के अंतिम संशोधित समय के आधार पर इसे कैश करता है, जिसे वह हर बार जांचता है जिसे यह कहा जाता है।
फिर आप एक दूसरा फ़ंक्शन जोड़ते हैं transformData()जो डेटा को रूपांतरित करता है, और अगली बार फ़ंक्शन को कॉल करने के लिए इसके परिणाम को कैश करता है। इसे फ़ाइल का कोई ज्ञान नहीं है - आप इस निर्भरता को कैसे जोड़ते हैं कि यदि फ़ाइल को बदल दिया जाता है, तो यह कैश अमान्य हो जाता है?

आप getData()हर बार कॉल कर सकते हैं transformData()और इसकी तुलना उस मूल्य से कर सकते हैं जो कैश बनाने के लिए इस्तेमाल किया गया था, लेकिन यह बहुत महंगा होने का कारण बन सकता है।


6
मेरा मानना ​​है कि वह
ग्रेग

1
मुझे लगता है कि शीर्षक "कैश इनवैलिडेशन - एक सामान्य समाधान है?" जैसा कि यह कैशिंग समस्या के एक विशिष्ट वर्ग को संदर्भित करता है।
RBarryYoung

71
नहीं, वह बहुत कंप्यूटर विज्ञान नहीं जानता था। मुझे यकीन है कि OpenGL, X11 और SSLv3 को बनाने में उसकी भागीदारी ने उसे वास्तव में बहुत अध्ययन करने में व्यस्त कर दिया। :-)
टिम लेशर

80
कंप्यूटर विज्ञान में केवल 2 कठिन समस्याएं हैं: कैश अमान्य होना। नामकरण की बातें। और एक-के-बाद एक त्रुटियां।
दाग

8
मैंने एक बार इसे सुना"The two hardest things in Computer Science are cache invalidation, naming things, and off-by-one errors."
जोनाथन रेनहार्ट

जवाबों:


55

आप जिस बारे में बात कर रहे हैं वह आजीवन निर्भरता की जंजीर है, कि एक चीज दूसरे पर निर्भर है जिसे उसके नियंत्रण से बाहर संशोधित किया जा सकता है।

आप से एक idempotent समारोह है, तो a, bके लिए cजहां, अगर aऔर bएक ही हैं तो cएक ही है, लेकिन जाँच की लागत bउच्च या तो फिर तुम है:

  1. यह स्वीकार करें कि आप कुछ समय की जानकारी के साथ काम करते हैं और हमेशा जाँच नहीं करते हैं b
  2. bजितनी जल्दी हो सके जाँच करने के लिए अपने स्तर पर सबसे अच्छा है

आप अपना केक नहीं खा सकते हैं और इसे खा सकते हैं ...

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

यदि आप कैश को परत करते हैं, तो आपको विचार करना चाहिए कि क्या आपने संयुक्त व्यवहार के परिणामस्वरूप सिस्टम के 'नियमों' का उल्लंघन किया है।

यदि आप जानते हैं कि aहमेशा वैधता होती है यदि bआप ऐसा करते हैं तो आप अपने कैश को व्यवस्थित कर सकते हैं जैसे (स्यूडोकोड):

private map<b,map<a,c>> cache // 
private func realFunction    // (a,b) -> c

get(a, b) 
{
    c result;
    map<a,c> endCache;
    if (cache[b] expired or not present)
    {
        remove all b -> * entries in cache;   
        endCache = new map<a,c>();      
        add to cache b -> endCache;
    }
    else
    {
        endCache = cache[b];     
    }
    if (endCache[a] not present)     // important line
    {
        result = realFunction(a,b); 
        endCache[a] = result;
    }
    else   
    {
        result = endCache[a];
    }
    return result;
}

जाहिर है लगातार लेयरिंग (माना x) जब तक कि मामूली बात है, प्रत्येक चरण में नए जोड़े गए इनपुट की वैधता से मेल खाता है a: bके लिए संबंध x: bऔर x: a

हालांकि यह काफी संभव है कि आप तीन इनपुट प्राप्त कर सकते हैं जिनकी वैधता पूरी तरह से स्वतंत्र थी (या चक्रीय थी), इसलिए कोई लेयरिंग संभव नहीं होगा। इसका मतलब होगा कि चिन्हित // महत्वपूर्ण को बदलना होगा

अगर (endCache [एक] समाप्त हो गई है या मौजूद नहीं)


3
या हो सकता है, यदि b की जाँच करने की लागत अधिक है, तो आप pubsub का उपयोग करते हैं ताकि जब b परिवर्तन हो तो यह c सूचित करता है। ऑब्जर्वर पैटर्न आम है।
user1031420

15

कैश अमान्य होने की समस्या यह है कि सामान हमारे बारे में जाने बिना बदल जाता है। इसलिए, कुछ मामलों में, एक समाधान संभव है यदि कोई अन्य चीज है जो इसके बारे में जानती है और हमें सूचित कर सकती है। दिए गए उदाहरण में, गेटडाटा फ़ंक्शन फ़ाइल सिस्टम में हुक कर सकता है, जो फ़ाइलों के सभी परिवर्तनों के बारे में जानता है, भले ही प्रक्रिया फ़ाइल को बदले, और यह घटक बदले में डेटा को बदलने वाले घटक को सूचित कर सकता है।

मुझे नहीं लगता कि समस्या को दूर करने के लिए कोई सामान्य जादू फिक्स है। लेकिन कई व्यावहारिक मामलों में "मतदान" के दृष्टिकोण को "रुकावट" में बदलने के लिए बहुत अच्छी तरह से अवसर हो सकते हैं, जो कि समस्या को सरलता से दूर कर सकता है।


3

यदि आप हर बार ट्रांस्फ़ॉर्म करने के लिए getData () में जा रहे हैं, तो आपने कैश के संपूर्ण लाभ को समाप्त कर दिया है।

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


3

IMHO, फ़ंक्शनल रिएक्टिव प्रोग्रामिंग (FRP) एक तरह से कैश अमान्यकरण को हल करने का एक सामान्य तरीका है।

यहाँ क्यों है: FRP शब्दावली में बासी डेटा को एक गड़बड़ कहा जाता है । FRP का एक लक्ष्य ग्लिच की अनुपस्थिति की गारंटी देना है।

FRP को इस 'सार का सार' बात में और इस SO उत्तर में अधिक विस्तार से समझाया गया है ।

में बात करते हैंCell एक कैश की गई वस्तु / इकाई का प्रतिनिधित्व करते हैं और एक Cellअगर यह की निर्भरता में से एक ताज़ा किया जाता है ताज़ा किया जाता है।

एफआरपी निर्भरता ग्राफ के साथ जुड़े प्लंबिंग कोड को छुपाता है और सुनिश्चित करता है कि कोई बासी नहीं हैं Cell


एक और तरीका (एफआरपी से अलग) जो मैं सोच सकता हूं कि bएक लेखक मोनाड के कुछ प्रकार में गणना मूल्य (प्रकार के ) को लपेट रहा है Writer (Set (uuid)) bजहां Set (uuid)(हास्केल अंकन) में उन सभी मूल्यों के पहचानकर्ता शामिल हैं जिन पर गणना मूल्य bनिर्भर करता है। तो, uuidकुछ प्रकार की एक अद्वितीय पहचानकर्ता है जो कि परिवर्तनशील मूल्य / चर (डेटाबेस में एक पंक्ति कहते हैं) की पहचान करता है, जिस पर संकलन bनिर्भर करता है।

कॉम्बिनेटरों के साथ इस विचार को मिलाएं जो इस तरह के लेखक मोनाड पर काम करते हैं और हो सकता है कि यह किसी प्रकार के सामान्य कैश अमान्य समाधान का कारण बन सकता है यदि आप केवल नए की गणना करने के लिए इन कॉम्बिनेटर का उपयोग करते हैं b। इस तरह के कॉम्बिनेटर (विशेष संस्करण कहते हैं filter) राइटर मोनाड्स और (uuid, a)-एस को इनपुट्स के रूप में लेते हैं, जहां aएक म्यूटेबल डेटा / वैरिएबल होता है, जिसकी पहचान की जाती है uuid

इसलिए हर बार जब आप "मूल" डेटा बदलते हैं (uuid, a)(एक डेटाबेस में सामान्यीकृत डेटा कहते हैं, जिसमें bसे गणना की गई थी) जिस पर प्रकार का परिकलित मान bनिर्भर करता है, तो आप उस कैश को अमान्य कर सकते हैं, जिसमें bआपके द्वारा किसी भी मान aको म्यूट किया जाता है जिस पर परिकलित bमान निर्भर करता है , क्योंकि Set (uuid)लेखक मोनाड में आधारित आप बता सकते हैं कि ऐसा कब होता है।

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

बेशक, यह केवल तभी भुगतान करता है जब आप लिखते हुए अधिक बार पढ़ते हैं।


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


2

मैं अभी PostSharp और मेमोइज़िंग फ़ंक्शन के आधार पर एक दृष्टिकोण पर काम कर रहा हूं । मैंने इसे अपने गुरु के अतीत में चलाया है, और वह इस बात से सहमत हैं कि यह सामग्री-अज्ञेय तरीके से कैशिंग का एक अच्छा कार्यान्वयन है।

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


1

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

नहीं, क्योंकि सभी डेटा अलग हैं। कुछ डेटा एक मिनट के बाद "बासी" हो सकते हैं, कुछ एक घंटे के बाद, और कुछ दिन या महीनों के लिए ठीक हो सकते हैं।

अपने विशिष्ट उदाहरण के बारे में, सबसे सरल उपाय फाइलों के लिए 'कैश चेकिंग' फ़ंक्शन है, जिसे आप दोनों से कॉल करते हैं getDataऔर transformData


1

कोई सामान्य समाधान नहीं है लेकिन:

  • आप कैश प्रॉक्सी (पुल) के रूप में कार्य कर सकते हैं। मान लें कि आपका कैश अंतिम मूल परिवर्तन का टाइमस्टैम्प जानता है, जब कोई व्यक्ति कॉल करता है getData(), तो कैश अंतिम मूल टाइमस्टैम्प के लिए उत्पत्ति पूछता है, यदि वही है, तो यह कैश लौटाता है, अन्यथा यह स्रोत एक के साथ अपनी सामग्री को अपडेट करता है और अपनी सामग्री वापस करता है। (भिन्नता सीधे अनुरोध पर टाइमस्टैम्प भेजने के लिए क्लाइंट है, स्रोत केवल सामग्री वापस करेगा यदि उसका टाइमस्टैम्प अलग हो।)

  • आप अभी भी एक अधिसूचना प्रक्रिया (पुश) का उपयोग कर सकते हैं, कैश स्रोत का निरीक्षण करता है, यदि स्रोत बदलता है, तो यह कैश को एक अधिसूचना भेजता है जिसे तब "गंदा" के रूप में चिह्नित किया जाता है। अगर कोई कहता है getData()कि कैश पहले स्रोत से अपडेट हो जाएगा, तो "गंदे" झंडे को हटा दें; फिर इसकी सामग्री वापस करें।

आमतौर पर बोलने का विकल्प इस पर निर्भर करता है:

  • आवृत्ति: कई कॉल पर getData()एक पुश पसंद करेंगे ताकि स्रोत से बचने के लिए गेटटीमस्टैम्प फ़ंक्शन द्वारा बाढ़ आ जाए
  • स्रोत तक आपकी पहुंच: क्या आप स्रोत मॉडल के मालिक हैं? यदि नहीं, तो संभावना है कि आप कोई अधिसूचना प्रक्रिया नहीं जोड़ सकते।

नोट: जैसा कि टाइमस्टैम्प का उपयोग कर रहा है पारंपरिक तरीका http प्रॉक्सी काम कर रहा है, एक अन्य दृष्टिकोण संग्रहीत सामग्री का एक हैश साझा कर रहा है। जिस तरह से मैं 2 संस्थाओं को एक साथ अपडेट करने के लिए जानता हूं, मैं या तो आपको कॉल करता हूं (पुल) या आप मुझे कॉल करते हैं ... (पुश) यह सब।


0

कैश कठिन है क्योंकि आपको विचार करने की आवश्यकता है: 1) कैश कई नोड्स हैं, उनके लिए सर्वसम्मति की आवश्यकता है 2) अमान्य समय 3) दौड़ की स्थिति जब मल्टीपल मिलता / सेट होता है

यह अच्छी रीडिंग है: https://www.confluent.io/blog/turning-the-database-inside-out-with-apache-samza/


-2

शायद कैश-अनजान एल्गोरिदम सबसे सामान्य होगा (या कम से कम, कम हार्डवेयर कॉन्फ़िगरेशन निर्भर), क्योंकि वे सबसे तेज़ कैश का उपयोग पहले करेंगे और वहाँ से आगे बढ़ेंगे। यहाँ इस पर एक एमआईटी व्याख्यान दिया गया है: कैश विस्मृत एल्गोरिदम


3
मुझे लगता है कि वह हार्डवेयर कैश के बारे में बात नहीं कर रहा है - वह अपने गेटडाटा () कोड के बारे में बात कर रहा है जिसमें एक फ़ाइल में मेमोरी से प्राप्त डेटा "कैश" है।
एलेक्स 319
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.