13

トラフィックの多いWebサイトがあり、休止状態を使用しています。また、ehcacheを使用して、ページの生成に必要ないくつかのエンティティとクエリをキャッシュします。

問題は「並列キャッシュミス」であり、長い説明は、アプリケーションが起動してキャッシュ領域がコールドになると、サイトが多くのユーザーにヒットされているため、各キャッシュ領域に異なるスレッドが何度も(1回だけではなく)入力されることです。同時に。さらに、一部のキャッシュ領域が無効になると、同じ理由で何度も再入力されます。どうすればこれを回避できますか?

hibernate.cache.provider_classに独自の実装を提供することで、1つのエンティティと1つのクエリキャッシュをBlockingCacheに変換できましたが、BlockingCacheのセマンティクスが機能していないようです。最悪の場合でも、BlockingCacheがデッドロック(ブロック)し、アプリケーションが完全にハングすることがあります。スレッドダンプは、get操作でBlockingCacheのミューテックスで処理がブロックされていることを示しています。

それで、問題は、Hibernateがこの種の使用をサポートしているのかということです。

そうでない場合は、本番環境でこの問題をどのように解決しますか?

編集hibernate.cache.provider_classは、 SingletonEhCacheProviderからのコピーペーストであり、start()メソッドの最後(136行目以降)にあるカスタムキャッシュプロバイダーを指します。

Ehcache cache = manager.getEhcache("foo");
if (!(cache instanceof BlockingCache)) {
    manager.replaceCacheWithDecoratedCache(cache, new BlockingCache(cache));
}

このように初期化すると、他の誰かが「foo」という名前のキャッシュに触れる前に、BlockingCacheで装飾します。「foo」はクエリキャッシュであり、「bar」(同じコードですが省略されています)はpojoのエンティティキャッシュです。

編集2:「機能していないようです」は、最初の問題がまだ存在することを意味します。同時実行性のため、キャッシュ「foo」には同じデータが何度も再入力されています。10スレッドのJMeterでサイトにストレスをかけることで、これを検証します。9つのスレッドは、「foo」からのデータを要求してジョブを終了し(クエリを実行し、データをキャッシュに格納する)、最初のスレッドがキャッシュから直接データを取得するまでブロックされると予想されます。

編集3:この問題の別の説明はで見ることができますhttps://forum.hibernate.org/viewtopic.php?f=1&t=964391&start=0ですが、明確な答えはありません。

4

2 に答える 2

5

よくわかりませんが、次のようになります。

これにより、すでにキャッシュにある要素への同時読み取りアクセスが可能になります。要素がnullの場合、同じキーを持つ要素がキャッシュに入れられるまで、他の読み取りはブロックされます。

これは、他のスレッドがオブジェクトをキャッシュに配置するまでHibernateが待機することを意味しませんか?それはあなたが観察することですよね?

Hibとキャッシュは次のように機能します。

  1. Hibはオブジェクトのリクエストを受け取ります
  2. Hibは、オブジェクトがキャッシュにあるかどうかをチェックします--cache.get()
  3. いいえ?HibはDBからオブジェクトをロードし、キャッシュに入れます--cache.put()

したがって、オブジェクトがキャッシュにない場合(以前の更新操作によってキャッシュに配置されていない場合)、Hibは1)を永久に待機します。

スレッドがオブジェクトを短時間だけ待機するキャッシュバリアントが必要だと思います。例:100ms。オブジェクトが到着しない場合、スレッドはnullになるはずです(したがって、HibernateはオブジェクトをDBからロードし、キャッシュに配置します)。

実際には、より良いロジックは次のようになります。

  1. 別のスレッドが同じオブジェクトを要求していることを確認してください
  2. trueの場合、オブジェクトが到着するまで長時間(500ms)待ちます
  3. trueでない場合は、すぐにnullを返します

(例外のために、スレッドがオブジェクトをキャッシュに入れることができない可能性があるため、2を永遠に待つことはできません)。

BlockingCacheがこの動作をサポートしていない場合は、自分でキャッシュを実装する必要があります。私は過去にそれをしました、それは難しいことではありません-主なメソッドはget()とput()です(APIはそれ以来明らかに成長していますが)。

アップデート

実際、BlockingCacheのソースを読んだだけです。それは私が言ったことを正確に実行します-ロックしてタイムアウトを待ちます。したがって、何もする必要はありません。使用するだけです...

public Element get(final Object key) throws RuntimeException, LockTimeoutException {
    Sync lock = getLockForKey(key);
    Element element;
        acquiredLockForKey(key, lock, LockType.WRITE);
        element = cache.get(key);
        if (element != null) {
            lock.unlock(LockType.WRITE);
        }
    return element;
}

public void put(Element element) {
    if (element == null) {
        return;
    }
    Object key = element.getObjectKey();
    Object value = element.getObjectValue();

    getLockForKey(key).lock(LockType.WRITE);
    try {
        if (value != null) {
            cache.put(element);
        } else {
            cache.remove(key);
        }
    } finally {
        getLockForKey(key).unlock(LockType.WRITE);
    }
}

ですから、それがあなたにとってうまくいかないのはちょっと奇妙です。何か教えてください:あなたのコードのこの場所:

Ehcache cache = manager.getEhcache("foo");

同期していますか?複数のリクエストが同時に発生した場合、キャッシュのインスタンスは1つだけになりますか?

于 2009-10-05T17:31:27.433 に答える
1

この問題の最大の改善点は、ehcacheが(2.1以降)休止状態のキャッシュポリシーをサポートするようになったtransactionalことです。これにより、この問題で説明されている問題が大幅に軽減されます。

さらに一歩進むには(同じクエリキャッシュ領域にアクセスしながらスレッドをロックする)、QueryTranslatorFactoryを実装して、クエリとパラメータを検査し、必要に応じてlistメソッドでブロックするカスタム(拡張)QueryTranslatorImplインスタンスを返す必要があります。もちろん、これは、多くのエンティティをフェッチするhqlを使用したクエリキャッシュの特定のユースケースに関係します。

于 2011-03-27T09:35:16.367 に答える