Önbellek Geçersiz Kılma - Genel Bir Çözüm Var mı?


118

"Bilgisayar Biliminde yalnızca iki zor sorun vardır: önbellek geçersiz kılma ve nesnelerin adlandırılması."

Phil Karlton

Bir önbelleği geçersiz kılmak için genel bir çözüm veya yöntem var mı; bir girişin ne zaman eski olduğunu bilmek, böylece her zaman yeni veriler almanız garantidir?

Örneğin, getData()bir dosyadan veri alan bir işlevi düşünün . Dosyayı, her çağrıldığında kontrol ettiği, dosyanın son değiştirilme zamanına göre önbelleğe alır.
Ardından transformData(), verileri dönüştüren ve bir sonraki işlev çağrıldığında sonucunu önbelleğe alan ikinci bir işlev eklersiniz . Dosya hakkında hiçbir bilgisi yoktur - dosya değiştirilirse bu önbelleğin geçersiz hale gelmesi bağımlılığını nasıl eklersiniz?

getData()Her transformData()çağrıldığında arayabilir ve önbelleği oluşturmak için kullanılan değerle karşılaştırabilirsiniz, ancak bu çok maliyetli olabilir.


6
Onun X Windows yazmakla ilgisi olduğuna inanıyorum
Greg

1
Bu başlığın "Önbellek Geçersiz Kılma - Genel Bir Çözüm Var mı?" Olarak daha iyi olacağını düşünüyorum. belirli bir önbelleğe alma sorunu sınıfına atıfta bulunduğundan.
RBarryYoung

71
Hayır, pek bilgisayar bilimi bilmiyordu. OpenGL, X11 ve SSLv3'ün yaratılmasına olan ilgisinin onu gerçekten çok çalışmak için çok meşgul ettiğinden eminim. :-)
Tim Lesher

80
Bilgisayar biliminde sadece 2 zor sorun vardır: Önbellek geçersiz kılma. Bir şeyleri adlandırmak. Ve tek tek hatalar.
The Dag

8
Bunu bir kez duymuştum"The two hardest things in Computer Science are cache invalidation, naming things, and off-by-one errors."
Jonathon Reinhart

Yanıtlar:


55

Bahsettiğiniz şey, ömür boyu bağımlılık zincirlemesidir, bir şey diğerine bağımlıdır ve bu, kendi kontrolü dışında değiştirilebilir.

Eğer bir İdempotent işlevi varsa a, biçin ceğer nerede ave baynı sonra caynı ama kontrol maliyeti bya yüksek o zaman geçerli:

  1. Bazen güncel olmayan bilgilerle çalıştığınızı ve her zaman kontrol etmediğinizi kabul edin b
  2. bmümkün olduğunca hızlı kontrol yapmak için seviyenizi en iyi şekilde yapın

Pastanı yiyip yiyemezsin ...

aEn üste dayalı olarak ek bir önbellek katmanlayabilirseniz, bu, ilk sorunu bir bit değil etkiler. 1'i seçerseniz, kendinize verdiğiniz özgürlüğü elde edersiniz ve böylece daha fazla önbelleğe alabilirsiniz, ancak önbelleğe alınan değerin geçerliliğini dikkate almayı unutmamalısınız b. 2'yi seçerseniz, yine de bher seferinde kontrol etmeniz gerekir, ancak kontrol aederse için önbelleğe geri dönebilirsiniz b.

Önbellekleri katmanlara ayırırsanız, birleşik davranışın bir sonucu olarak sistemin 'kurallarını' ihlal edip etmediğinizi dikkate almalısınız.

Bunun aher zaman geçerliliği olduğunu biliyorsanız, bönbelleğinizi şu şekilde düzenleyebilirsiniz (sözde kod):

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;
}

Açıktır ki x, her aşamada yeni eklenen girdinin geçerliliği a: bilişkisinin x: bve x: ile eşleştiği sürece ardışık katmanlama (diyelim ) önemsizdir a.

Bununla birlikte, geçerliliği tamamen bağımsız (veya döngüsel) olan üç girdi almanız oldukça olasıdır, bu nedenle katmanlama mümkün olmaz. Bu, önemli olarak işaretlenen satırın // değişmesi gerektiği anlamına gelir

(endCache [a] 'nın süresi dolmuşsa veya yoksa)


3
veya belki, b'yi kontrol etmenin maliyeti yüksekse, pubsub'ı kullanırsınız, böylece b değiştiğinde c'yi bilgilendirir. Gözlemci modeli yaygındır.
user1031420

15

Önbellek geçersiz kılmadaki sorun, şeylerin biz bilmeden değişmesidir. Yani, bazı durumlarda, bunu bilen ve bize bildirebilecek başka bir şey varsa bir çözüm mümkündür. Verilen örnekte, dosyadaki tüm değişiklikleri bilen dosya sistemine, hangi işlemin dosyayı değiştirdiğine bakılmaksızın getData işlevi bağlanabilir ve bu bileşen de verileri dönüştüren bileşene bildirimde bulunabilir.

Sorunu ortadan kaldıracak genel bir sihirli çözüm olduğunu sanmıyorum. Ancak birçok pratik durumda, "yoklama" tabanlı bir yaklaşımı "kesinti" tabanlı bir yaklaşıma dönüştürmek için çok iyi fırsatlar olabilir ve bu da sorunu basitçe ortadan kaldırabilir.


3

Dönüşümü her yaptığınızda data () alacaksanız, önbelleğin tüm faydasını ortadan kaldırmış olursunuz.

Örneğiniz için, dönüştürülmüş verileri oluşturduğunuzda, verilerin oluşturulduğu dosyanın dosya adını ve son değiştirilme zamanını da depolamak için bir çözüm gibi görünüyor (bunu, getData (getData ( ), bu nedenle bu kaydı transformData ()) tarafından döndürülen veri yapısına kopyalayın ve sonra transformData () öğesini tekrar çağırdığınızda, dosyanın son değiştirilme zamanını kontrol edin.


3

IMHO, Fonksiyonel Reaktif Programlama (FRP), bir anlamda önbellek geçersiz kılmayı çözmenin genel bir yoludur.

İşte nedeni: FRP terminolojisindeki eski verilere aksaklık denir . FRP'nin hedeflerinden biri, aksaklıkların olmamasını garanti etmektir.

FRP, bu 'FRP'nin Özü' konuşmasında ve bu SO cevabında daha ayrıntılı olarak açıklanmıştır .

Gelen konuşCell s önbelleğe alınmış Nesne / Varlık temsil etmek ve bir Cell's bağımlılık biri yenilenir eğer yenilenir.

FRP, bağımlılık grafiğiyle ilişkili tesisat kodunu gizler ve eski URL'lerin bulunmadığından emin olur Cell.


Düşünebildiğim başka bir yol (FRP'den farklı), hesaplanan değeri (türünün b) bir tür yazıcıya sarmaktır Writer (Set (uuid)) b; burada Set (uuid)(Haskell gösterimi), hesaplanan değerin bbağlı olduğu değişken değerlerin tüm tanımlayıcılarını içerir . Öyleyse, uuidhesaplananın bbağlı olduğu değişken değeri / değişkeni (bir veritabanındaki bir satır gibi) tanımlayan bir tür benzersiz tanımlayıcıdır .

Bu fikri, bu tür bir yazıcı Monad üzerinde çalışan birleştiricilerle birleştirin ve bu birleştiricileri yalnızca yeni bir hesaplamak için kullanırsanız, bir tür genel önbellek geçersiz kılma çözümüne yol açabilir b. Bu tür birleştiriciler (özel bir versiyonu diyelim filter) Writer monadlarını ve -s'yi (uuid, a)girdiler olarak alırlar , burada adeğiştirilebilir bir veri / değişken, tarafından tanımlanır uuid.

Bu nedenle , hesaplanan tür değerinin bağlı olduğu "orijinal" verileri (uuid, a)(örneğin b, hesaplanan bir veritabanındaki normalleştirilmiş verileri ) her değiştirdiğinizde , hesaplanan değerin bağlı olduğu herhangi bir değeri değiştirirseniz b, içeren önbelleği geçersiz kılabilirsiniz. , çünkü Writer Monad'a dayanarak bunun ne zaman gerçekleştiğini anlayabilirsiniz.babSet (uuid)

Yani bir verili bir şeyi her mutasyona uğrattığınızda uuid, bu mutasyonu tüm cache-lere yayınlarsınız ve bunlar b, belirtilen değişken değere bağlı olan değerleri geçersiz kılar uuidçünkü biçine sarılmış olan Writer monad, bunun bsöylenen uuidveya değil.

Tabii ki, bu sadece yazdığınızdan çok daha sık okursanız işe yarar.


Üçüncü, pratik bir yaklaşım, veri tabanlarında somutlaşmış görünümleri kullanmak ve bunları önbellek olarak kullanmaktır. AFAIK ayrıca hükümsüzlük sorununu çözmeyi amaçlamaktadır. Bu elbette, değiştirilebilir verileri türetilmiş verilere bağlayan işlemleri sınırlar.


2

Şu an dayalı bir yaklaşım üzerinde çalışıyorum PostSharp ve memoizing fonksiyonları . Bunu akıl hocamın önünden geçirdim ve içerikten bağımsız bir şekilde önbelleğe almanın iyi bir uygulaması olduğunu kabul ediyor.

Her işlev, sona erme süresini belirten bir öznitelikle işaretlenebilir. Bu şekilde işaretlenen her işlev hafızaya alınır ve sonuç, işlev çağrısının bir karması ve anahtar olarak kullanılan parametreler ile önbelleğe kaydedilir. Önbellek verilerinin dağıtımını işleyen arka uç için Velocity kullanıyorum .


1

Bir önbellek oluşturmanın, bir girişin ne zaman eski olduğunu bilmek, böylece her zaman yeni veriler almanızın garantili olması için genel bir çözüm veya yöntem var mı?

Hayır, çünkü tüm veriler farklıdır. Bazı veriler bir dakika sonra "eski" olabilir, bazıları bir saat sonra olabilir ve bazıları günler veya aylarca sorunlu olabilir.

Spesifik örneğinizle ilgili olarak, en basit çözüm, hem getDatave hem de den çağırdığınız dosyalar için bir 'önbellek denetimi' işlevine sahip olmaktır transformData.


1

Genel bir çözüm yok ama:

  • Önbelleğiniz bir proxy (çekme) görevi görebilir. Önbelleğinizin son kaynak değişikliğinin zaman damgasını bildiğini varsayın, biri aradığında getData(), önbellek son değişikliğin zaman damgasını sorar, aynıysa önbelleği döndürür, aksi takdirde içeriğini kaynakla günceller ve içeriğini döndürür. (Bir varyasyon, istemcinin doğrudan istek üzerine zaman damgasını göndermesidir, kaynak yalnızca zaman damgası farklıysa içerik döndürür.)

  • Hala bir bildirim işlemi (push) kullanabilirsiniz, önbellek kaynağı gözlemler, kaynak değişirse, önbelleğe bir bildirim gönderir ve bu daha sonra "kirli" olarak işaretlenir. Eğer birisi getData()önbelleği ararsa önce kaynağa güncellenir, "kirli" bayrağını kaldırın; sonra içeriğini iade edin.

Genel anlamda seçim şunlara bağlıdır:

  • Frekans: birçok çağrı getData(), kaynağın getTimestamp işlevi tarafından doldurulmasını önlemek için bir itmeyi tercih eder.
  • Kaynağa erişiminiz: Kaynak modele sahip misiniz? Değilse, muhtemelen herhangi bir bildirim işlemi ekleyemezsiniz.

Not: Zaman damgasını kullanmak http proxy'lerinin geleneksel çalışma şekli olduğundan, başka bir yaklaşım depolanan içeriğin bir karmasını paylaşmaktır. 2 varlığın birlikte güncellenmesinin bildiğim tek yolu ya seni ararım (çek) ya da sen beni ara… (it) hepsi bu.



-2

Belki de önbellekten habersiz algoritmalar en genel olanıdır (veya en azından donanım yapılandırmasına daha az bağımlıdır), çünkü ilk önce en hızlı önbelleği kullanacaklar ve oradan devam edecekler. İşte bununla ilgili bir MIT dersi: Önbellek Bilinmez Algoritmaları


3
Sanırım donanım önbelleklerinden bahsetmiyor - bir dosyadan aldığı verileri belleğe "önbelleğe alan" bir özelliğe sahip getData () kodundan bahsediyor.
Alex319
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.