2009-06-11 8 views
8

Mamy aplikację przepływu pracy Java, która korzysta z bazy danych Oracle do śledzenia jej kroków i interakcji z innymi usługami. Podczas przepływu pracy wykonywanych jest kilka operacji insert/update/select, a od czasu do czasu select nie zwróci zaktualizowanych danych, nawet jeśli zatwierdzenie insert/update, które zostało uruchomione, zanim zostało pomyślnie zakończone. Po usunięciu błędów przepływu pracy (z powodu złych danych), jeśli wrócimy i sprawdzimy bazę danych za pośrednictwem aplikacji innej firmy, pojawią się nowe/zaktualizowane dane. Wydaje się, że występuje opóźnienie między momentem, w którym popełniane są nasze zobowiązania i kiedy są one widoczne. Dzieje się to w około 2% wszystkich przepływów pracy i zwiększa się podczas intensywnego korzystania z bazy danych.Opóźnienie Oracle między zatwierdzeniem i wybraniem

Nasz zespół wsparcia bazy danych zaproponował zmianę parametru max-commit-propagation-delay na 0, ponieważ domyślnie był ustawiony na 700. Wydaje się, że jest to możliwe rozwiązanie, ale ostatecznie nie rozwiązało problemu.

Aplikacja działa w środowisku WebSphere, a baza danych Oracle jest skonfigurowana jako źródło danych JDBC. Używamy Oracle 10.1g. Aplikacja napisana jest w Javie 1.5.

Każda pomoc zostanie doceniona.

edit: przykładowy kod

DataSource ds; // spring configured 

String sql = "INSERT INTO " + currentTable + " (" + stepId + ',' + stepEntryId + ", " + stepStepId + ", " + stepActionId + ", " + stepOwner + ", " + stepStartDate + ", " + stepDueDate + ", " + stepFinishDate + ", " + stepStatus + ", " + stepCaller + ") VALUES (?, ?, ?, null, ?, ?, ?, null, ?, null)"; 

Connection conn = ds.getConnection(); 
PreparedStatement stmt = conn.prepareStatement(sql); 
// set values 
stmt.executeUpdate(); 
// close connections 

// later on in the code... 
Connection conn = ds.getConnection(); 
PreparedStatement stmt = null; 
ResultSet rset = null; 

String sql = "SELECT " + stepId + ", " + stepStepId + ", " + stepActionId + ", " + stepOwner + ", " + stepStartDate + ", " + stepDueDate + ", " + stepFinishDate + ", " + stepStatus + ", " + stepCaller + " FROM " + currentTable + " WHERE " + stepEntryId + " = ?"; 
stmt = conn.prepareStatement(sql); 

stmt.setLong(1, entryId); 

rset = stmt.executeQuery(); 
//close connections 
+0

Z [Dokumentacja Oracle] (http://download.oracle.com/docs/cd/B14117_01/server.101/b10755/initparams115.htm) wynika, że ​​parametr 'max_commit_propagation_delay' ma zastosowanie tylko do konfiguracji RAC. Czy łączysz się z instancją RAC? –

+0

Czy dane są zatwierdzane jako część transakcji? Lub czytać? –

Odpowiedz

1

są użycie przy użyciu ORM? może wybierać z pamięci podręcznej i nie tworzyć bazy danych po zmianie.

+0

Nie używamy ORM, tabele i instrukcje sql są naprawdę podstawowe i są używane tylko do przechowywania niewielkiej ilości informacji o śledzeniu. – Andrew

+0

umieść fragment kodu, jeśli to możliwe ... –

+1

, jeśli sugestie @Steve Broberg nie działają, możesz spróbować użyć tego samego połączenia –

6

Domyślnie opisane zachowanie powinno być niemożliwe - zmiany wprowadzone w zatwierdzonej transakcji są natychmiast dostępne dla wszystkich sesji. Jednak istnieją wyjątki:

  1. używasz żadnej z opcji zapisu, zgodnie z poleceniem COMMIT? Jeśli nie, potwierdź wartość parametru inicjowania COMMIT_WRITE. Jeśli używasz "WRITE BATCH" lub w szczególności "WRITE BATCH NOWAIT", możesz otworzyć się na problemy z współbieżnością. "WRITE BATCH NOWAIT" będzie zwykle używany w przypadkach, gdy szybkość twoich transakcji zapisu jest ważniejsza niż możliwe problemy z współbieżnością. Jeśli parametr inicjalizacji za pomocą warianty „Write”, można zastąpić go na bazie transakcji, określając bezpośrednim klauzulę w swoim zatwierdzeń (see COMMIT)

  2. Czy transakcja, która próbuje odczytać dane powołujące zbioru transakcji przed do popełnienia drugiej transakcji? Korzystanie zbioru transakcji określić serializacji LEVEL READ ONLY lub SERIALIZABLE będzie skutkować transakcja nie widząc zmiany, które pojawiają się od innych zaangażowanych sesjach, które wystąpiły po wywołaniu zbioru transakcji (see SET TRANSACTION)

edit: widzę, że ciebie używają klasy DataSource. Nie znam tej klasy - zakładam, że jest to zasób udostępniania połączenia. Zdaję sobie sprawę, że twój obecny projekt aplikacji może nie ułatwiać korzystania z tego samego obiektu połączenia przez cały czas pracy (kroki mogą być zaprojektowane do niezależnej pracy, a ty nie zbudowałeś obiektu, aby przekazać obiekt połączenia od jednego kroku do następne), ale powinieneś sprawdzić, czy obiekty połączeń zwracane do obiektu DataSource są "czyste", szczególnie w odniesieniu do otwartych transakcji. Możliwe, że nie wywołujesz SET TRANSACTION w swoim kodzie, ale może to zrobić inny użytkownik DataSource w innym miejscu i przywracając połączenie do źródła danych z sesją wciąż w trybie SERIALIZABLE lub READ ONLY. Podczas udostępniania połączenia konieczne jest wycofanie wszystkich połączeń przed przekazaniem ich nowemu konsumentowi.

Jeśli nie masz kontroli lub widoczności dla zachowania klasy DataSource, możesz spróbować uruchomić ROLLBACK na nowo nabytym połączeniu, aby upewnić się, że nie ma już ustalonej transakcji.

+0

+1 !!! Nie brałem nawet pod uwagę, że funkcja COMMIT może być inna niż NATYCHMIASTOWA CZEKAĆ lub że poziom izolacji transakcji był inny niż READ COMMITTED. – spencer7593

+0

Przepraszamy za opóźnienie w skontaktowaniu się z Tobą. Myślę, że nie używam żadnych opcji zapisu dla zatwierdzenia. Używam tylko tego, do czego domyślnie jest przypisana klasa połączenia Java, to samo dotyczy ustawienia izolacji transakcji. Oto niektóre łącza dokumentacji dla klas java, których używam: http://java.sun.com/j2se/1.5.0/docs/api/javax/sql/DataSource.html http: //java.sun .com/j2se/1.5.0/docs/api/java/sql/Connection.html http://java.sun.com/j2se/1.5.0/docs/guide/jdbc/getstart/connection.html – Andrew

4

Jeśli zespół DBA próbował zmodyfikować parametr max_commit_propagation_delay, prawdopodobnie oznacza to, że łączysz się z instancją RAC (i-e: kilka różnych serwerów uzyskujących dostęp do jednej bazy danych).

W takim przypadku, gdy zamykasz i ponownie otwierasz połączenie w kodzie Java, istnieje szansa, że ​​otrzymasz odpowiedź od innego serwera. Parametr opóźnienia oznacza, że ​​istnieje mały przedział czasowy, w którym dwie instancje nie będą dokładnie w tym samym punkcie w czasie. Odpowiedź, którą otrzymujesz, jest zgodna z punktem w czasie, ale może nie być najbardziej aktualna.

Zgodnie z propozycją KM najprostszym rozwiązaniem byłoby pozostawienie połączenia otwartego po zatwierdzeniu.

Można również dodać opóźnienie po zamknięciu połączenia, jeśli jest to praktyczne (jeśli jest to zadanie wsadowe, a czas reakcji nie jest na przykład istotny).

0

To brzmi jak problem z RAC, z połączeniami do dwóch różnych instancji i SCN jest niezsynchronizowany.

Aby obejść ten problem, należy rozważyć nie zamykanie połączenia z bazą danych i uzyskiwanie nowego, ale ponowne użycie tego samego połączenia.

Jeśli to nie działa, należy dodać próbę do zapytania, które próbuje pobrać wstawiony wiersz. Jeśli wiersz nie zostanie zwrócony, spocznij trochę i ponów próbę zapytania. Połóż to w pętli, po określonej liczbie prób, możesz następnie zawieść.

[DODATEK]

W swojej odpowiedzi, Steve Broberg (+1!) Rodzi ciekawe pomysły. Nie uznała:

  • COMMIT może być coś innego niż IMMEDIATE WAIT
  • poziom izolacji transakcji może być coś innego niż odczyt zatwierdzony

Zrobiłem rozważyć możliwość zapytania retrospekcji, a oddalił to od razu, nie wspominając o tym, ponieważ nie ma żadnego oczywistego powodu, dla którego OP będzie używał retrospekcyjnego zapytania, i nie ma żadnych dowodów na coś takiego w fragmencie kodu.)

[/ ADDENDUM]

0

Możliwe obejście może być użycie transakcji JTA. Utrzymuje połączenie otwarte "za sceną" przez wiele otwartych/zamkniętych stajni jdbc. Może to utrzyma twoje połączenie na tym samym serwerze i uniknie tego problemu z synchronizacją.

UserTransaction transaction = (UserTransaction)new InitialContext().lookup("java:comp/UserTransaction"); 
transaction.begin(); 
// doing multiple open/close cxs 
transaction.commit(); 
0

Fragment kodu w rzeczywistości nie zawierał zatwierdzenia.

Jeśli zakładasz/powołujesz się na bliskie połączenie wykonując commit, może on nie być synchroniczny (tzn. Java może zgłosić, że połączenie jest zamknięte, gdy mówi Oracle, aby zamknąć połączenie, co oznacza, że ​​może to być przed commit jest uzupełniany przez Oracle).

+0

http : //java.sun.com/j2se/1.5.0/docs/api/java/sql/Connection.html Domyślnie obiekt połączenia jest w trybie automatycznego zatwierdzania, co oznacza, że ​​automatycznie zatwierdza zmiany po wykonaniu każdego z nich. komunikat. – Andrew

+0

Yuck. Ale to samo może mieć zastosowanie, ponieważ nie masz kontroli nad tym, jak/kiedy zatwierdzanie jest wykonywane. Poza tym za popełnianie niepotrzebnie nałożono karę wykonania (i prawdopodobnie konsekwencje integralności danych). Wyłącz to, wyraź jawnie i sprawdź, czy problem zniknie. –

0

Nie widzę żadnego zatwierdzenia w Twoim kodzie.Są to najważniejsze stwierdzenia w takiej aplikacji, więc chciałbym, aby były wyraźnie napisane za każdym razem, nie polegając na close() lub podobnym.

Możesz również automatycznie ustawić autocommit na true w twoich połączeniach, które dokładnie wyjaśniają zachowanie (zatwierdza się po każdym wstawieniu/aktualizacji).

Czy możesz sprawdzić, czy dokonałeś zatwierdzenia dokładnie tam, gdzie chcesz, np. na koniec transakcji, a nie wcześniej?

Jeśli po częściowym przejściu dokonano zatwierdzenia, oznacza to, że pomiędzy nitkami występuje stan wyścigu, co również wyjaśniałoby, dlaczego jest więcej problemów, gdy obciążenie jest większe.

+0

Mam ustawione auto-commit na true, chociaż nie jestem pewien, jak to by wyjaśniało zachowanie, które widzę. Wykonuję insert i executeUpdate(), które automatycznie zatwierdza, a następnie wykonuje select i ten wstawiony wiersz nie został znaleziony. Nie widzę tutaj, jak może się zdarzyć sytuacja wyścigu, biorąc pod uwagę, że executeUpdate() nie zwróci, dopóki nie zostanie zatwierdzony. – Andrew

0

"mimo że zatwierdzenie wstawiania/aktualizacji, które działało, zanim zostało pomyślnie zakończone."

Sugeruje to, że wydajesz commit(), a następnie spodziewasz się ponownie odczytać dokładnie te same dane (to jest czytanie powtarzalne).

Sugeruje to, że nie powinieneś się angażować. Tak długo, jak chcesz upewnić się, że ŻADNE INNE ZADANIE nie jest w stanie zmodyfikować żadnych DANYCH, KTÓRYCH WYCZYNUJESZ OCZEKUJ, aby pozostały stabilne, nie możesz pozwolić sobie na zwolnienie blokady (co robi zatwierdzenie).

Należy pamiętać, że podczas blokowania niektórych zasobów inne wątki będą układać się w stosy "oczekując na udostępnienie tego zasobu". Prawdopodobieństwo, że ten stos nie będzie pusty w momencie zwolnienia blokady, wzrośnie wraz ze wzrostem ogólnego obciążenia systemu. A co powiesz na temat DBMS, kiedy (ostatecznie) wydasz polecenie "commit", dojdziesz do wniosku, że "hej, wow, ten facet jest w końcu gotowy z tym zasobem, więc teraz mogę pozwolić wszystkim innym oczekującym facetom spróbować i zrobić ich rzeczy (i nie ma NIC, aby zapobiec "ich rzeczy" od aktualizacji!) ".

Być może są problemy z izolacją migawki Oracle, którą przeoczyłem. Przepraszam, jeśli tak.

Powiązane problemy