2009-07-30 14 views
4

これは若干学問的かもしれませんが、アプリケーションを高速化するためにキャッシュを実装すると、キャッシュミスをどのように処理するのが最適でしょうか? (私の場合は、言語はJavaのだろう、多分答えは、より一般的なことができます)ハンドルキャッシュの欠落:NotFoundException、contains()または `if(null == result)`?

例外をスロー:

ResultType res; 
if (Cache.contains(someKey) { 
    res = Cache.resLookup(someKey); 
} else { 
    res = Cache.resInsert(someKey, SlowDataSource.resLookup(someKey)); 
} 

戻りヌル:

ResultType res; 
try { 
    res = Cache.resLookup(someKey); 
} catch (NotFoundException e) { 
    res = Cache.resInsert(someKey, SlowDataSource.resLookup(someKey)); 
} 

フェッチする前に確認します:

ResultType res; 
res = Cache.resLookup(someKey); 
if (null == res) { 
    res = Cache.resInsert(someKey, SlowDataSource.resLookup(someKey)); 
} 

例外をスローすると結局、これはエラーではないようです。キャッシュにcontains()を検索させてから再度データを取得することは、特にこれが毎回発生するので、無駄に見えます。そしてもちろんのnullをチェックするnullは有効な結果になることはありませんことを要求...

答えて

6

最初のものは過剰だと思いますが、例外としてはあまり役に立ちません。キャッシュヒットが起こると予想していますか?キャッシュミスは、私が思うとかなり例外的に発生するもので、例外は単にフロー制御になります。いいえ、イホ。

第2の競合状態です。キャッシュエントリの存在を確認してからクエリを実行するまでに時間がかかります。それはあらゆる種類のトラブルにつながる可能性があります。

nullを返すのはおそらく一般的な意味では適切ですが、それにはいくつかの資格があります。

まず、どのタイプのキャッシュですか?理想的には、リードスルーキャッシュと話すのは理想的です。キャッシュにない場合は、ソースからコードを取得します。コードのスタイルではありません。

第2に、getとinsertは別の競合条件です。この種のものを扱う良い一般的な方法については、​​のインタフェースを見てください。特に、putIfAbsent()の呼び出しは、3番目の呼び出しであることに相当するアトミック操作です。

+0

競争条件、おかげで良い点。しかし、第三でもないのですか? –

+0

はい、3番目にも競合条件があります。読み込み後、読み込んだ直後(nullとして)に、挿入できるようになる前にキャッシュに置かれたエントリを上書きすることが条件です。 – Chii

+0

3番目の例では、競合状態は適切ですが、同じ結果が見つかるはずなので、キャッシュに正しい結果がない方法はないため、どのようにして誰に悪影響を及ぼすかはわかりません。 – tomjen

1

あなたがうっかりそれを無視して、誤ってヌルを返すことができませんので、私は、チェック例外の方が多いでしょう。私は(ここのほとんどの人と違って)キャッシュミスは珍しいシナリオだと仮定しています。

また、キャッシュの内部実装についても説明しています。クライアントの観点から見ると、これは不可視でなければなりません。

+0

はい、これはキャッシュの内部にあります。コードはそれを明確にしません。申し訳ありません。 –

+0

-1。最初に値が使用されると、キャッシュには格納されません。したがって、キャッシュミスは例外的ではなくNORMALです。 – KitsuneYMG

+0

-1チェック例外の場合 – cletus

4

私の意見では、最後のオプション(null == resultの場合)が最適です。

キャッシュミスは例外的な条件ではなく、通常のコードフローで処理する必要があります。

キャッシュ内に何かが存在するかどうかを確認する動作が多少高価な操作(例えば、memcached呼び出しのネットワークオーバーヘッド)である場合、別の操作であってはなりません。また、キャッシュがスレッド間で共有されている場合、アイテムを実際に取得する前にcontains()の値が変更されることがあります。

+0

私は同意します、キャッシュが見落とされたときに例外をスローするのは、最初に新しい値を使うときに起こるので、悪い考えです。 – KitsuneYMG

0

キャッシュすることで既存のAPIをスピードアップしているので、目に見えないもの(第2のもの)が適用されます。私はapiが存在すると仮定しているので、これを言います。あなたは時期尚早に最適化していません。

0

(3)は、(これらの選択肢の間の実際のパフォーマンスの違いは無視できないほどですが)読みやすいと思います。

(2)では、キャッシュ内で2つのルックアップを行う必要があります(最初は "contains"、次に "resLookup")。

(1)追加のオブジェクトを作成すると、コードは複雑になり、読みにくくなり、キャッシュミスも例外ではありません。

+0

ケース1の追加オブジェクトは何ですか? –

+0

null値を許可する例では、NotFoundException –

0

キャッシュミスが例外なく、通常のケースではありません

  1. 「クリアコードの視点から。だから私はここで例外を使用しませんでした。
  2. 可能な場合はnull値を避ける必要があります。 「フェッチする前に確認する」の代わり
  3. このオプションを残して意味のある何かまたはキャッシュミスに

例に信号を送り、あなたのあなたは「NULL」返さない第三の選択肢が、特別なresultTypeとオブジェクトのバリエーションを選択してください:

public class ResultType { 

    public final static ResultType CACHE_MISS = new ResultType(); 

    // ... rest of the class implementation 

} 

以降 'NULL' ソリューションを超える

ResultType res; 
res = Cache.resLookup(someKey); 
if (ResultType.CACHE_MISS == res) { 
    res = Cache.resInsert(someKey, SlowDataSource.resLookup(someKey)); 
} 

優位に

:ハンドル 'が' 読者は、今すぐそれを知っているのですキャッシュミス。

2

第4のオプションはどうですか?あなたは、戻り値のためのホルダーを使用して、検索を持っている成功のためのブール値を返すことができます:あなたが値としてnullを持っていると思った場合

ResultHolder result = new ResultHolder(); 
if(!cache.resLookup(someKey, result)) 
{ 
    // get from slower source and insert to cache 
} 

if(result.value == null) 
{ 
    // special case if you wanted null as a valid value 
} 

これは基本的にあなたの第三の選択肢は、ある単一の呼び出しを保つが、あなた可能性があります。

+0

+1です。 –

関連する問題