2012-09-27 19 views
26

Zapomnijmy najpierw o hibernacji. Załóżmy, że mam dwie tabele, A & B. Dwie transakcje aktualizują te same rekordy w tych dwóch tabelach, ale txn 1 aktualizację B, a następnie A, podczas gdy txn2 aktualizację A, a następnie B. Jest to typowy przykład impasu. Najczęstszym sposobem uniknięcia tego jest wstępne zdefiniowanie kolejności pozyskiwania zasobów. Na przykład powinniśmy zaktualizować tabelę A, a następnie B.W jaki sposób Hibernacja decyduje o kolejności aktualizacji/wstawienia/usunięcia

Powróć do trybu hibernacji. Gdy aktualizujemy wiele jednostek w jednej sesji, po opróżnieniu sesji, zmiany różnych podmiotów wygenerują odpowiednie instrukcje wstawiania/aktualizacji/usuwania do bazy danych. Czy Hibernate ma jakiś algorytm decydujący o kolejności aktualizacji między jednostkami? Jeśli nie, w jaki sposób Hibernate używał do zapobiegania sytuacji zakleszczenia opisanej w pierwszym akapicie?

Jeśli hibernacja utrzymuje zamówienie, w jaki sposób mogę znać lub kontrolować zamówienie? Nie chcę, aby moja jawna aktualizacja w DB powodowała konflikty z Hibernate i powodowała zakleszczenie.

Odpowiedz

29

Problem, który opisujesz, nie jest obsługiwany przez bazę danych iz mojego doświadczenia nie jest w pełni obsługiwany przez Hibernate.

Należy podjąć wyraźne kroki w celu uniknięcia problemu.

Hibernacja wykonuje niektóre prace za Ciebie. Zgodnie z poprzednią odpowiedzią, Hibernate zapewnia, że ​​w obrębie wydzielonego spłukiwania wstawki, usuwanie i aktualizacje są sortowane w sposób zapewniający ich zastosowanie w możliwej do zrealizowania kolejności.Zobacz performExecutions(EventSource session) w klasie AbstractFlushingEventListener:

Wykonaj wszystkie SQL (i drugiego poziomu cache aktualizacji) w specjalnym porządku, tak że ograniczenia zagranicznych klucz nie może być naruszone:

  1. wkładki, w kolejności ich przeprowadzono
  2. Aktualizacje
  3. skreślenia elementów kolekcji
  4. wkładania elementów kolekcji
  5. usuwa w kolejności, w jakiej zostały wykonane

Kiedy posiadające unikalne ograniczeń To bardzo ważne, aby wiedzieć, ten porządek, zwłaszcza, jeśli chcesz zastąpić dziecku jeden-do-wielu (usunąć stary/wstawić nowy), ale zarówno stare, jak i nowe dziecko mają te same unikalne ograniczenia (np ten sam adres e-mail). W takim przypadku możesz zaktualizować stary wpis, zamiast usuwać/wstawiać, lub możesz opróżnić tylko po usunięciu, aby kontynuować wstawianie. Bardziej szczegółowy przykład można sprawdzić this article.

Należy pamiętać, że nie określa ona kolejności aktualizacji. Przeanalizowanie kodu hibernacji skłania mnie do przekonania, że ​​kolejność aktualizacji zależy od kolejności dodania encji do kontekstu utrwalania, kolejność, w jakiej zostały zaktualizowane. To może być przewidywalne w twoim kodzie, ale odczytanie kodu Hibernacji nie pozostawiło we mnie wrażenia, że ​​będę polegać na tym zamówieniu.

Istnieją trzy rozwiązania można myślę:

  1. spróbuj ustawić hibernate.order_updates być prawda. Powinno to pomóc w uniknięciu zakleszczeń podczas aktualizowania wielu wierszy w tej samej tabeli, ale nie pomoże w przypadku zakleszczeń w wielu tabelach.
  2. Przed dokonaniem jakichkolwiek aktualizacji dokonaj transakcji przy użyciu blokady PESSIMISTIC_WRITE. To, której podmiotu używasz, zależy od konkretnej sytuacji, ale dopóki zapewniasz, że podmiot zostanie wybrany konsekwentnie, jeśli istnieje ryzyko zakleszczenia, zablokuje to resztę transakcji, aż do uzyskania blokady.
  3. Napisz swój kod, aby złapać zakleszczenia, gdy wystąpią i spróbuj ponownie w rozsądny sposób. Komponent zarządzający ponownym zablokowaniem blokady musi znajdować się poza bieżącą granicą transakcji. Dzieje się tak dlatego, że sesja niepowodzenia musi zostać zamknięta, a powiązana z nią transakcja wycofana. W dokumencie this article można znaleźć przykład automatycznego ponawiania aspektu AOP.
+1

Proszę przyjąć moje przeprosiny, że z nieznanego powodu tęskniłem za tą odpowiedzą przed :) Wierzę, że wynik twojego egzaminu w kodzie Hibernate'a rzeczywiście daje najbardziej zaufaną odpowiedź do tej pory. Dobre sugestie, szczególnie dla punktu 1 i 2 –

-1

Odnośnie pierwszego przykładu - tego rodzaju rzeczy są obsługiwane przez bazę danych (więcej informacji na temat poziomów izolacji transakcji i strategii blokowania bazy danych). Istnieje wiele różnych sposobów rozwiązania tego problemu.

Co do hibernacji, javadoc do org.hibernate.event.def.AbstractFlushingEventListener.performExecutions (EventSource) mówi:

wykonać wszystkie aktualizacje SQL i pamięci podręcznej drugiego poziomu, na specjalne zamówienie, tak że ograniczenia klucza obcego nie może być naruszone:

  1. Wstawki w kolejności, w jakiej zostały wykonane
  2. Aktualizacje Usuwanie elementów kolekcji
  3. Wstawienie elementów kolekcji
  4. Usuwa, w kolejności, w jakiej zostały wykonane

Zakładam, że jest to jedyna optymalizacja wykonywanych zapytań SQL Hibernacja czyni. Reszta problemów jest obsługiwana przez bazę danych.

+0

Wierzę, że pierwszy przykładowy materiał NIE jest w większości w stanie obsłużyć bazy danych. Dla poziomu izolacji, z mojego zrozumienia wynika, że ​​zmiana jednej transakcji jest widoczna dla innych. Jednak nawet dla najniższego poziomu izolacji, dwa zapisy transakcji w tym samym wierszu nadal będą blokowane. Dlatego jeśli jeden txn aktualizuje rekord A, następnie B, a inny txn aktualizuje B, a następnie A, nadal będzie tworzył zakleszczenie. Najlepsze, co DBMS może zrobić, to wykryć potencjalny zakleszczenie. Jestem pewien, że wspomniałeś o poleceniu wykonania Hibernowanego SQL, ale o co mi chodzi, to około –

+0

kolejność aktualizacji między elementami. Będzie to dość zaskakujące, jeśli Hibernate nie będzie obsługiwał tego i założy, że może być obsługiwane przez DB (co oczywiście jest nierealistyczne) –

+0

Oracle na przykład w jakiś sposób radzi sobie z zakleszczeniami "automatycznie wykrywa i rozwiązuje zakleszczenia poprzez wycofywanie instrukcji związanych z transakcją który wykrywa zakleszczenie ": http://www.oracle-base.com/articles/misc/deadlocks.php.Prawdopodobnie spowoduje to wyjątek JDBC, więc możesz dodać logikę ponowną w kodzie obsługującym wyjątek. – Potejciak