7

キャッシュしたい非常に高価な計算があります。したがって、次のようなことを行います。

my $result = $cache->get( $key );

unless ($result) {
    $result = calculate( $key );
    $cache->set( $key, $result, '10 minutes' );
}

return $result;

ここでcalculate($key)、結果をキャッシュに保存する前に、他のいくつかの要求が入ってきて実行を開始しcalculate($key)、多くのプロセスがすべて同じことを計算するため、システム パフォーマンスが低下します。

アイデア: 値が計算されていることを示すフラグをキャッシュに入れましょう。他のリクエストはその 1 つの計算が完了するのを待つだけなので、すべてのリクエストがそれを使用します。何かのようなもの:

my $result = $cache->get( $key );

if ($result) {
    while ($result =~ /Wait, \d+ is running calculate../) {
        sleep 0.5;
        $result = $cache->get( $key );
    }
} else {
    $cache->set( $key, "Wait, $$ is running calculate()", '10 minutes' );
    $result = calculate( $key );
    $cache->set( $key, $result, '10 minutes' );
}


return $result;

これにより、まったく新しいワームの缶が開かれます。$$ がキャッシュを設定する前に死亡した場合はどうなりますか。もしも、もし...それらはすべて解決可能ですが、CPANにはこれを行うものは何もないため(CPANにはすべてのものがあります)、私は疑問に思い始めます:

より良いアプローチはありますか?CachePerlやCache::Cacheクラスがこのようなメカニズムを提供していないなど、特定の理由はありますか? 代わりに使用できる実証済みのパターンはありますか?

理想的なのは、すでにスクイーズまたは eureka の瞬間にある debian パッケージを含む CPAN モジュールです。ここで、自分のやり方のエラーが表示されます... :-)

編集:これがキャッシュスタンピードと呼ばれることを知り、質問のタイトルを更新しました。

4

4 に答える 4

2

flock()それ。

calculate()ワーカープロセスはすべて同じシステム上にあるため、高価なイオンをシリアル化するために、古き良きファイルロックを使用できる可能性があります。ボーナスとして、このテクニックはいくつかのコアドキュメントに記載されています。

use Fcntl qw(:DEFAULT :flock);    # warning:  this code not tested

use constant LOCKFILE => 'you/customize/this/please';

my $result = $cache->get( $key );

unless ($result) {
    # Get an exclusive lock
    my $lock;
    sysopen($lock, LOCKFILE, O_WRONLY|O_CREAT) or die;
    flock($lock, LOCK_EX) or die;

    # Did someone update the cache while we were waiting?
    $result = $cache->get( $key );

    unless ($result) {
        $result = calculate( $key );
        $cache->set( $key, $result, '10 minutes' );
    }

    # Exclusive lock released here as $lock goes out of scope
}

return $result;

利点:労働者の死亡は即座にを解放し$lockます。

リスク:LOCK_EXは永久にブロックされる可能性があり、それは長い時間です。SIGSTOPを避け、おそらくに慣れてくださいalarm()

拡張機能:すべてのcalculate()呼び出しをシリアル化するのではなく、同じ$keyまたはいくつかのキーのセットに対するすべての呼び出しをシリアル化する場合は、ワーカーがシリアル化できflock() /some/lockfile.$key_or_a_hash_of_the_keyます。

于 2012-05-23T15:43:33.030 に答える
1

ロックを使用しますか?それともそれはやり過ぎでしょう?または、可能であれば、結果をオフラインで事前計算してからオンラインで使用しますか?

于 2012-05-22T23:53:36.080 に答える
1

あなたのユースケースではやり過ぎかもしれませんが (またはそうでないかもしれません)、処理にメッセージキューを使用することを検討しましたか? 現在、 RabbitMQは Perl コミュニティで人気のある選択肢のようで、AnyEvent::RabbitMQモジュールでサポートされています。

この場合の基本的な戦略はcalculate、新しいキーが必要なときはいつでもメッセージ キューにリクエストを送信することです。calculate確実に処理できるのであれば、一度に (要求された順序で) 1 つのキーのみにキューを設定できます。または、複数のキーを同時に安全に計算できる場合は、キューを使用して同じキーに対する複数のリクエストを統合し、一度計算してそのキーをリクエストしたすべてのクライアントに結果を返すこともできます。

もちろん、これは少し複雑になり、AnyEvent は、あなたが慣れ親しんでいるものとは多少異なるプログラミング スタイルを呼び出します (例を示しますが、私自身は実際にコツをつかんだことはありません)。効率性と信頼性が十分に向上し、これらのコストを価値あるものにすることができます。

于 2012-05-23T13:40:27.957 に答える
-1

上記のピルクロウのアプローチに一般的に同意します。私はそれに 1 つ追加します:memoize()関数の使用を調査して、コード内の操作を高速化する可能性calculate()があります。

詳細については、 http://perldoc.perl.org/Memoize.htmlを参照してください。

于 2012-05-28T07:12:23.320 に答える