2009-09-22 18 views
5

Pracuję nad aplikacją internetową połączoną z Oracle. Mamy stół w wyroczni z kolumną "aktywowaną". Tylko jeden wiersz może mieć tę kolumnę ustawioną na 1 w tym samym czasie. Aby to wymusić, w Javie używaliśmy poziomu izolacji SERIALIZED, jednak używamy błędu "nie można serializować transakcji" i nie możemy ustalić przyczyny.READ COMMITTED poziom izolacji bazy danych w Oracle

Zastanawialiśmy się, czy wykonanie zadania READ COMMITTED byłoby wystarczające. Więc moje pytanie brzmi:

Jeśli mamy transakcję, która obejmuje następujące SQL:

SELECT * 
FROM MODEL; 

UPDATE MODEL 
SET ACTIVATED = 0; 

UPDATE MODEL 
SET ACTIVATED = 1 
WHERE RISK_MODEL_ID = ?; 

COMMIT; 

Biorąc pod uwagę, że jest to możliwe dla więcej niż jednego z tych transakcji podlegających wykonaniu w tym samym czasie, to być możliwe dla więcej niż jednego wiersza MODELu, aby aktywowana flaga była ustawiona na 1?

Każda pomoc zostanie doceniona.

Odpowiedz

3

Twoje rozwiązanie powinno działać: pierwsza aktualizacja zablokuje całą tabelę. Jeśli inna transakcja nie zostanie zakończona, aktualizacja będzie czekać. Twoja druga aktualizacja zagwarantuje, że tylko jeden wiersz będzie miał wartość 1, ponieważ blokujesz tabelę (nie blokuje to jednak instrukcji INSERT).

Należy również upewnić się, że wiersz z RISK_MODEL_ID istnieje (lub na końcu transakcji będzie mieć wiersz zerowy o wartości "1").

Aby zapobiec współbieżnym instrukcjom INSERT, należy LOCK tabelę (w TRYBIE EXCLUSIVE).

+0

Podobny, ale ja LOCK wzór w wyłącznych MODE i zrobić aktualizację MODEL SET aktywne = 0 gdzie aktywowane = 1 Mniej wiersze są aktualizowane, więc jest to nieco bardziej wydajnych, a tabela LOCK uniemożliwia aktywność współbieżne na stole. –

3

Można rozważyć zastosowanie unikalnego, funkcja indeksu opartego pozwolić Oracle obsługiwać ograniczenie: tylko o jeden wiersz z aktywowanym flaga ustawiona na 1.

CREATE UNIQUE INDEX MODEL_IX ON MODEL (DECODE(ACTIVATED, 1, 1, NULL)); 

Byłoby zatrzymać więcej niż jeden wiersz o flagę ustawiona na 1, ale nie oznacza to, że zawsze jest jeden wiersz z flagą ustawioną na 1.

+0

+1: prosty, wydajny, bezpieczny dla wielu użytkowników –

2

Jeśli chcesz, aby tylko jedna transakcja mogła działać jednocześnie, możesz użyć składni FOR UPDATE. Ponieważ masz jeden wiersz, który wymaga blokowania, jest to bardzo efektywne podejście.

declare 
    cursor c is 
     select activated 
     from model 
     where activated = 1 
     for update of activated; 
    r c%rowtype; 
begin 
    open c; 
    -- this statement will fail if another transaction is running 
    fetch c in r; 
    .... 
    update model 
    set activated = 0 
    where current of c; 

    update model 
    set activated = 1 
    where risk_model_id = ?; 

    close c; 

    commit; 
end; 
/

commit uwalnia blokadę.

Domyślnym ustawieniem jest oczekiwanie, aż wiersz zostanie zwolniony. W przeciwnym razie możemy określić NOWAIT, w takim przypadku każda inna sesja próbująca zaktualizować bieżący aktywny wiersz natychmiast się nie powiedzie, lub możemy dodać opcję WAIT z czasem odpytywania. NOWAIT to opcja, która pozwala całkowicie uniknąć ryzyka powieszenia, a także daje nam szansę poinformowania użytkownika, że ​​ktoś inny aktualizuje tabelę, co może być potrzebne.

To podejście jest znacznie bardziej skalowalne niż aktualizacja wszystkich wierszy w tabeli. Użyj wskaźnika opartego na funkcjach, gdy WW pokazał, że wymusza regułę, że tylko jeden wiersz może mieć AKTYWOWANĄ = 1.

+0

+1: ładne rozwiązanie –

Powiązane problemy