2015-01-15 9 views
6

Załóżmy, że mam dwa różne wątki, T1 i T2, dostęp do jednocześnie tej samej bazy danych i pobieranie danych z tej samej tabeli.Jak uniknąć dwóch różnych wątków przeczytać te same wiersze z DB (Hibernate i Oracle 10g)

Teraz przy uruchamianiu wątku muszę pobrać dane z tabeli i zapisać wiersze w kolekcji, którą następnie wykorzystam do wykonania pracy w innym miejscu. Nie chcę, aby te dwa wątki mogły przetwarzać te same dane, ponieważ spowoduje to powielenie (i długą) pracę. Aby być bardziej konkretnym, jest to aplikacja dla przedsiębiorstw, która musi załadować niektóre rekordy podczas uruchamiania i przechowywać ją w kolekcji, aby wykonać dodatkową pracę. Problem polega na tym, że w środowisku klastrowym może to spowodować, że dwa różne wystąpienia załadują te same dane, a zatem praca może zostać powielona. Dlatego chcę, aby wiersze były ładowane tylko raz przez pojedynczą instancję.

Jak mogę uniknąć tego scenariusza?

Używam obecnie Hibernacji i Oracle 10g. Oto moje dotychczasowe rozwiązania:

  • Programowo zablokuj wiersz. Pierwszy, który go czyta, ustawia pewną "zamkniętą" kolumnę na wartość prawda, ale zakleszczenie najprawdopodobniej nastąpi, jeśli pierwszy wątek umrze bez ustawiania wiersza jako "przetworzony".

  • Korzystanie z pesymistycznego blokowania. Próbowałem z LockMode.UPGRADE, ale to nie wydaje się pomóc, ponieważ wciąż jestem w stanie odczytać dane z obu wątków w tym samym czasie.

public List<MyObject> getAllNtfFromDb() { 
     Session session = HibernateUtil.getOraclesessionfactory().openSession(); 
     Query q = session.createQuery(
       "from MyObject n where n.state = 'NEW'"); 
    List<MyObject> list = (List<MyObject>) q.list(); 
     for (int i=0; i<list.size(); i++) 
      session.lock(list.get(i), LockMode.UPGRADE); 
return list; 
} 

Wszelkie inne wskazówki? Co ja robię źle?

Dzięki.

+1

Może możesz użyć instrukcji select for update? – soilworker

Odpowiedz

6

Trzeba użyć PESSIMISTIC_WRITE w czasie kwerendy:

Query q = session 
    .createQuery("from MyObject n where n.state = 'NEW'") 
    .setLockOptions(new LockOptions(LockMode.PESSIMISTIC_WRITE)); 
List<MyObject> list = (List<MyObject>) q.list(); 

Blokowanie obiektów nadrzędnych jest wystarczająca. Zakleszczenia niekoniecznie muszą wystąpić. Możesz uzyskać niepowodzenie przechwytywania blokady, jeśli wątek trzymający blokadę nie zwalnia go przed kolejnym wątkiem od momentu oczekiwania.

Ponieważ używasz Oracle, to jak SELECT FOR UPDATE utwory:

SELECT ... FOR UPDATE blokuje wierszy i wszelkie związane z indeksu wpisy, tak samo jak gdybyś wydał oświadczenie UPDATE dla tych wierszy . Inne transakcje są blokowane przed aktualizacją tych wierszy, począwszy od wykonania SELECT ... LOCK IN SHARE MODE, lub od odczytania danych na określonych poziomach izolacji transakcji . Stałe odczyty ignorują wszelkie blokady ustawione na rekordów istniejących w widoku odczytu. (Stare wersje rekordu nie może być zablokowany;. Oni są rekonstruowane przez zastosowanie cofnąć dzienniki na kopii w pamięci rekordu)

Więc jeśli T1 uzyskał wyłączną blokadę na niektórych wierszy, T2 wygrał” • móc odczytać te zapisy, dopóki T1 nie popełni lub nie wycofa się. Jeśli T2 użyje poziomu izolacji READ_UNCOMMITTED, to T2 nigdy nie będzie blokował rekordów kłódki, ponieważ używa po prostu logi cofania do rekonstrukcji danych tak, jakby były w momencie rozpoczęcia zapytania.W przeciwieństwie do standardu SQL The Oracke READ_UNCOMMITTED będą:

W celu zapewnienia spójnego, czy prawidłowe, odpowiedź, Oracle Database utworzyć kopię bloku zawierającego ten wiersz jak to miało miejsce, gdy zaczął zapytanie . ... W rzeczywistości baza danych Oracle zbiera objazd wokół zmodyfikowanych danych - czyta wokół niej, rekonstruując go z segmentu cofania (znanego również jako wycofanie). Spójna i prawidłowa odpowiedź powraca, nie czekając na zatwierdzenie transakcji.

+1

OK dziękuję, to działa, ale tylko wtedy, gdy dwa wątki próbują odczytać te same dane bardzo blisko w czasie. Jeśli T1 odczyta dane i po 1 minucie T2 odczyta te same dane (które T1 najprawdopodobniej nadal przetwarza), wówczas T2 może zobaczyć wiersze. Chcę zablokować wiersz, dopóki nie zostaną zaktualizowane (np. Kolumna stanu ustawiona na "OK" lub "PROCESSED"). Czy aktualizacja jednego obiektu powoduje automatyczne zwolnienie wiersza? A jeśli sesja zostanie zamknięta lub wątek ulegnie awarii, wiersze zostaną zwolnione po pewnym czasie oczekiwania? Dziękuję bardzo. – user2799534

+0

Prawdopodobnie zależy to od wyłącznej obsługi blokady bazy danych. –

Powiązane problemy