2009-10-05 8 views
13

Mam witrynę o dużym natężeniu ruchu i używam hibernacji. Używam również ehcache do buforowania niektórych encji i zapytań, które są wymagane do generowania stron.Unikanie wielokrotnych repopulacji tego samego regionu pamięci podręcznej (z powodu współbieżności)

Problem polega na "równoległych chybionych pamięciach podręcznych", a długie wyjaśnienie polega na tym, że gdy aplikacja jest uruchamiana, a obszary pamięci podręcznej są zimne, każdy obszar pamięci podręcznej jest zapełniany wielokrotnie (zamiast tylko raz) przez różne wątki, ponieważ witryna jest używana trafiony przez wielu użytkowników w tym samym czasie. Ponadto, gdy jakiś region pamięci podręcznej unieważnia, jest on wielokrotnie zapełniany z tego samego powodu. Jak mogę tego uniknąć?

Udało mi się uzyskać convert 1 entity and 1 query cache to a BlockingCache, zapewniając własną implementację hibernate.cache.provider_class, ale semantyka BlockingCache nie działa. Nawet najgorsze czasami blokady Deadlocks Blocking (bloki) i aplikacja zawiesza się całkowicie. Zrzut wątku pokazuje, że przetwarzanie jest blokowane na muteksie BlockingCache podczas operacji pobierania.

Pytanie brzmi, czy Hibernate obsługuje ten rodzaj użytkowania?

A jeśli nie, jak rozwiązać ten problem przy produkcji?

Edit: The hibernate.cache.provider_class punkty do mojego niestandardowego dostawcy cache, która jest kopią pasta z SingletonEhCacheProvider i na końcu metody start() (po linii 136) zrobić:

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

W ten sposób po inicjalizacji i zanim ktokolwiek dotknie pamięci podręcznej o nazwie "foo", ozdobię ją blokowaniem. "foo" to pamięć podręczna zapytań i "pasek" (ten sam kod, ale pominięty) to pamięć podręczna jednostek dla pojo.

Edytuj 2: "Wydaje się nie działać" oznacza, że ​​początkowy problem nadal istnieje. Pamięć podręczna "foo" wciąż jest wielokrotnie ponownie zapełniana tymi samymi danymi ze względu na współbieżność. Potwierdzam to, podkreślając witrynę za pomocą JMeter z 10 wątkami. Oczekuję, że 9 wątków zablokuje się do pierwszego, który zażądał danych z "foo", aby zakończyć zadanie (wykonać zapytania, przechowywać dane w pamięci podręcznej), a następnie pobrać dane bezpośrednio z pamięci podręcznej.

Edycja 3: Innym wyjaśnieniem tego problemu może być postrzegane w https://forum.hibernate.org/viewtopic.php?f=1&t=964391&start=0 ale bez jednoznacznej odpowiedzi.

+0

Interesującym rozwiązaniem, które może pomóc złagodzić ten problem, jest to, że teraz ehcache (od wersji 2.1) obsługuje strategię współbieżności pamięci podręcznej transakcji dla hibernacji: http://stackoverflow.com/questions/3472613/does-ehcache-2-1-support -transakcyjna-cache-współbieżność-strategia-w-hibernat/3474011 # 3474011 – cherouvim

Odpowiedz

1

Największym postępem w tej kwestii jest to, że teraz ehcache (od 2.1) supports the transactional hibernate cache policy. To znacznie zmniejsza problemy opisane w tym numerze.

Aby pójść o krok dalej (zablokować wątki podczas uzyskiwania dostępu do tego samego obszaru pamięci podręcznej zapytań), należy wprowadzić QueryTranslatorFactory, aby zwrócić niestandardowe (rozszerzone) instancje QueryTranslatorImpl, które sprawdzałyby zapytanie i parametry oraz blokować w razie potrzeby na liście metoda. Oczywiście dotyczy to konkretnego przypadku użycia pamięci podręcznej zapytań przy użyciu hql, który pobiera wiele elementów.

5

Nie jestem pewny, ale:

Umożliwia jednoczesny dostęp do elementów już w pamięci podręcznej odczytu. Jeśli element ma wartość NULL, inne odczyty będą blokowane, aż element z tym samym kluczem zostanie umieszczony w pamięci podręcznej.

Czy to nie oznacza, że ​​Hibernate będzie czekać, aż inny wątek umieści obiekt w pamięci podręcznej? To właśnie obserwujesz, prawda?

Hib i cache działa tak:

  1. Hib dostaje prośby o obiekcie
  2. sprawdza Hib jeśli obiekt znajduje się w pamięci podręcznej - cache.get()
  3. Nie? Hib ładuje obiekt z DB i umieszcza w pamięci podręcznej - cache.put()

Więc jeśli obiekt nie znajduje się w pamięci podręcznej (nie umieszczonego tam przez jakiś poprzedniej operacji update), Hib będzie czekać na 1) na zawsze.

Myślę, że potrzebujesz wariantu pamięci podręcznej, w którym wątek czeka tylko na obiekt przez krótki czas. Na przykład. 100 ms. Jeśli obiekt nie zostanie dostarczony, wątek powinien uzyskać wartość null (a więc Hibernacja ładuje obiekt z bazy danych i umieszcza go w pamięci podręcznej).

Faktycznie, lepiej logika byłaby:

  1. Sprawdź, czy inny wątek żąda tego samego obiektu
  2. Jeśli to prawda, czekać na długie (500 ms) dla obiektu przyjazd
  3. Jeśli nie prawda , zwróć natychmiast null

(Nie możemy czekać na 2 na zawsze, ponieważ wątek może nie umieścić obiektu w pamięci podręcznej - z powodu wyjątku).

Jeśli BlockingCache nie obsługuje tego zachowania, musisz zaimplementować pamięć podręczną samodzielnie. Zrobiłem to w przeszłości, to nie jest trudne - głównymi metodami są get() i put() (chociaż API najwyraźniej od tego czasu się rozwinęło).

UPDATE

Faktycznie, ja po prostu czytać źródła BlockingCache. Robi dokładnie to, co powiedziałem - zamknij i czekaj na timeout. Dlatego nie musisz nic robić, po prostu użyj ...

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

To trochę dziwne, że to nie działa. Powiedz mi coś: w swoim kodzie to miejsce:

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

czy jest zsynchronizowany? Jeśli wiele żądań przychodzi jednocześnie, czy będzie tylko jedno wystąpienie pamięci podręcznej?

+0

Dziękuję za odpowiedź. Nadal trudno mi uwierzyć, że muszę rozwiązać problem tak bardzo na poziomie hibernacji i ehcache. – cherouvim

+0

Nie jest zsynchronizowany, ale jest wywoływany tylko raz. Żyje pod publicznym ostatecznym nieważnym początkiem BlockingCacheProvider, który jest wywoływany tylko raz, zanim wszystko się zacznie. – cherouvim

Powiązane problemy