2010-11-19 14 views
16

Mam kilka wystąpień aplikacji J2EE uruchomionych w jednym klastrze WebLogic.Czy mogę wykonać atomową MERGE w Oracle?

W pewnym momencie aplikacje te wykonują MERGE, aby wstawić lub zaktualizować rekord do wewnętrznej bazy danych Oracle. MERGE sprawdza, czy istnieje wiersz z określonym kluczem podstawowym. Jeśli tam jest, zaktualizuj. Jeśli nie, włóż.

Załóżmy teraz, że dwie instancje aplikacji chcą wstawić lub zaktualizować wiersz z kluczem podstawowym = 100. Załóżmy, że wiersz nie istnieje. Podczas etapu "sprawdzenia" scalania oboje widzą, że nie ma tam wierszy, więc obaj próbują wstawić. Wtedy otrzymuję wyjątkowe naruszenie ograniczenia klucza.

Moje pytanie brzmi następująco: Czy w Oracle jest atomowa MERGE? Szukam czegoś, co ma podobny efekt do INSERT ... FOR UPDATE w PL/SQL, z tym wyjątkiem, że mogę wykonywać SQL z moich aplikacji.

EDYCJA: Byłem niejasny. Używam instrukcji MERGE, gdy ten błąd nadal występuje. Rzecz w tym, że tylko część "modyfikująca" jest atomowa, a nie cała fuzja.

+2

Scalenie jest atomowe. Działa albo nie działa jako kompletna jednostka pracy. To, co widzisz, jest wynikiem wdrożenia przez firmę Oracle spójności w zakresie konwersji wielu wersji. Wygląda na to, że szukasz czegoś, co umożliwi seralizację wielu scaleń? Możesz spróbować użyć transakcjalizowanych transakcji Oracle, ale to prawdopodobnie wystarczy zmienić błąd z unikalnego ograniczenia klucza, aby nie serializować błędu transakcji. –

+0

Moja baza danych/wiele wątków może być błędna. Moje zrozumienie to "praca lub porażka jako kompletna jednostka pracy" nazywana jest transakcyjną. Według atomisty chodziło mi o to, aby nie doszło do scalenia, podczas gdy inne scalanie jest przetwarzane. O serializacji scaleń, będę musiał przeczytać o tym. Dzięki. – Russell

+1

@Russell, atomowość to tylko jedna właściwość transakcji. I oznacza sukces lub porażkę jako jednostkę. Zobacz przykład: http://en.wikipedia.org/wiki/ACID i http://download.oracle.com/docs/cd/E11882_01/server.112/e16508/transact.htm#sthref1298. –

Odpowiedz

6

Instrukcja MERGE w drugiej sesji nie może "zobaczyć" wstawki, którą wykonała pierwsza sesja, dopóki ta sesja nie zostanie zatwierdzona. Jeśli zmniejszysz wielkość transakcji, prawdopodobieństwo, że do tego dojdzie, zostanie zmniejszone.

Czy można sortować lub partycjonować dane tak, aby wszystkie zapisy danego klucza podstawowego były przekazywane do tej samej sesji. Prosta funkcja, taka jak "klucz podstawowy mod N" powinna być równo rozłożona na N sesji.

btw, jeśli dwa rekordy mają ten sam klucz podstawowy, drugi zastąpi pierwszy. Brzmi trochę dziwnie.

+0

+1 dla obejścia na poziomie aplikacji. Dzięki. – Russell

+2

To nie odpowiada na pytanie. Obejście problemu nie zadziała, jeśli chcesz zsynchronizować dwie osobne aplikacje, które mają tylko db jako wspólny element. – Dariusz

3

Tak, i to się nazywa .... MERGE

EDIT: Jedynym sposobem, aby ta woda jest napięty do wstawienia, złapać wyjątek dup_val_on_index i obsługiwać go odpowiednio (aktualizacji lub włóż inny rekord może). Można to łatwo zrobić za pomocą PL/SQL, ale nie możesz tego użyć.

Szukacie również rozwiązań tymczasowych. Czy możesz złapać dup_val_on_index w Javie i ponownie wydać dodatkową aktualizację?

W pseudo-kod:

try { 
    // MERGE 
} 
catch (dup_val_on_index) { 
    // UPDATE 
} 
+0

Hahaha dzięki :) Zmieniliśmy to pytanie, aby wyjaśnić. – Russell

+0

OK, przyjrzę się jeszcze trochę po moim weekendzie! –

+0

Czy potrafisz używać PL/SQL? Pierwsza rzecz, którą znalazłem sugeruje, że wstawienie z obsługą wyjątków (gdy dup_val_on_index następnie aktualizuje) jest jedynym sposobem, aby uzyskać taką szczelność. –

2

Jestem zaskoczony, że MERGE będzie zachowywać się tak, jak opisać, ale nie używał go na tyle, by stwierdzić, czy powinien czy nie.

W każdym przypadku transakcje, które chcą wykonać zestaw scalania, mają poziom izolacji na SERIALIZABLE. Myślę, że to może rozwiązać twój problem.

13

To nie jest problem z MERGE jako takim. Raczej problem leży w twojej aplikacji. Rozważmy tę procedurę przechowywaną:

create or replace procedure upsert_t23 
    (p_id in t23.id%type 
     , p_name in t23.name%type) 
is 
    cursor c is 
     select null 
     from t23 
     where id = p_id; 
    dummy varchar2(1); 
begin 
    open c; 
    fetch c into dummy; 
    if c%notfound then 
     insert into t23 
      values (p_id, p_name); 
    else 
     update t23 
      set name = p_name 
      where id = p_id; 
    end if; 
end; 

To jest odpowiednik PL/SQL dla MERGE na T23. Co się dzieje, gdy dwie sesje nazywają to jednocześnie?

SSN1> exec upsert_t23(100, 'FOX IN SOCKS') 

SSN2> exec upsert_t23(100, 'MR KNOX') 

SSN1 trafia tam pierwszy, nie znajduje pasującego rekordu i wstawia rekord. SSN2 trafia tam na sekundę, ale przed zatwierdzeniem SSN1 nie znajduje rekordu, wstawia rekord i zawiesza, ponieważ SSN1 ma blokadę unikalnego węzła indeksowania dla 100. Gdy SSN1 zatwierdza SSN2, spowoduje to naruszenie DUP_VAL_ON_INDEX.

Instrukcja MERGE działa dokładnie w ten sam sposób. Obie sesje sprawdzą on (t23.id = 100), nie znajdą go i przejdą do gałęzi INSERT. Pierwsza sesja się powiedzie, a druga zarzuci ORA-00001.

Jednym ze sposobów rozwiązania tego problemu jest wprowadzenie pesymistycznego blokowania.Na początku procedury UPSERT_T23 możemy zablokować tabeli:

... 
lock table t23 in row shared mode nowait; 
open c; 
... 

Teraz SSN1 przybywa, chwyta blokadę i przebiega jak poprzednio. Gdy nadchodzi SSN2, nie może uzyskać blokady, więc natychmiast się zawiesza. Co jest frustrujące dla drugiego użytkownika, ale przynajmniej nie zwisa, a oni wiedzą, że ktoś inny pracuje nad tym samym rekordem.

Nie ma żadnej składni INSERT, która jest równoważna SELECT ... FOR UPDATE, ponieważ nie ma nic do wyboru. I tak nie ma takiej składni dla MERGE. Co musisz zrobić, to dołączyć instrukcję LOCK TABLE w jednostce programu, która wydaje komunikat MERGE. To, czy to możliwe, zależy od struktury, z której korzystasz.

+1

Dzięki, to była doskonała artykulacja tego, co próbowałem wyjaśnić w moim pytaniu. O tym, jak poradziłbyś sobie z problemem, czy nie jest częścią PL/SQL-a? Szukam obejścia/rozwiązania wykorzystującego SQL Oracle, który można wywołać z mojej aplikacji. Naprawdę nie szukałem INSERT, który jest równoważny SELECT ... FOR UPDATE (chociaż sposób, w jaki to napisałem, mógł skłonić cię do myślenia w ten sposób).Właśnie dowiedziałem się, czy istnieją jakieś sposoby na zablokowanie wiersza, który jest scalany przez instrukcję MERGE (stąd tytuł pytania). Mimo wszystko, dziękuję za dokładną odpowiedź i wiedzę :) – Russell

+1

Zablokowanie stołu w Rzędzie Podziel się (nie Row Shared) prawie nic nie daje. Uniemożliwi to kolejnej sesji uzyskanie wyłącznej blokady stołu. Nie uniemożliwi to kolejnej sesji aktualizacji/wstawienia/usunięcia. btw, blokada udziału w wierszu jest automatycznie pobierana po wydaniu polecenia "wybierz ... dla aktualizacji". Tryb "Udostępnij wiersz wyłączny" uniemożliwi innym sesjom zmienianie tabeli i nie będzie można go uzyskać, jeśli inna sesja zawiera niezatwierdzone zmiany. To prawie tak samo restrykcyjne jak wyłączna blokada. Używanie go zapobiega wszystkim jednoczesnym aktualizacjom - takim jak system "jednego użytkownika". – redcayuga

Powiązane problemy