2010-08-30 6 views
13

Jestem zainteresowany tym, czy zapytanie select for update zablokuje nieistniejący wiersz.Czy "wybierz do aktualizacji" uniemożliwia wstawianie innych połączeń, gdy wiersz nie jest obecny

np.

Tabela FooBar z dwóch kolumn, Foo i baru, Foo posiada unikalny wskaźnik

  • zapytanie Problem select bar from FooBar where foo = ? for update
  • Jeśli zapytanie zwraca zera rzędy
    • zapytanie Problem insert into FooBar (foo, bar) values (?, ?)

Teraz to możliwe e, że wkładka spowodowałaby naruszenie indeksu lub uniemożliwiłaby to select for update?

Interesuje zachowanie na SQLServer (2005/8), Oracle i MySQL.

+0

Does MSSQL nawet wsparcie tej składni? –

+0

Ma "z updlockiem", który moim zdaniem jest prawie taki sam. –

Odpowiedz

7

W Oracle SELECT ... FOR UPDATE nie ma wpływu na nieistniejący wiersz (instrukcja po prostu podnosi wyjątek No Data Found). Instrukcja INSERT uniemożliwi duplikaty unikatowych/podstawowych wartości kluczy. Wszelkie inne transakcje próbujące wstawić te same wartości klucza będą blokowane do momentu zatwierdzenia pierwszej transakcji (w tym czasie zablokowana transakcja otrzyma zduplikowany błąd klucza) lub wycofania (w tym czasie zablokowana transakcja będzie kontynuowana).

+0

Chciałbym ponownie część o "oświadczenie po prostu podnosi wyjątek znaleziono danych"; a SELECT ... FOR UPDATE może zwrócić wiersze i nadal nie ma wpływu na nieistniejący wiersz. ;) –

+0

Nie w przypadku PO, gdzie wybór był na foo = explicit_value, foo jest unikalnie indeksowany. – DCookie

+0

Dla instrukcji SELECT można zwrócić 0 wierszy. Podnosi NO_DATA_FOUND tylko wtedy, gdy jest wywoływany za pomocą INTO. – jva

0

Serwer SQL ma tylko FOR UPDATE jako część kursora. Dotyczy to również instrukcji UPDATE powiązanych z bieżącym wierszem kursora.

Tak więc, FOR UPDATE nie ma żadnego związku z INSERT. Dlatego myślę, że twoja odpowiedź brzmi, że nie ma zastosowania w SQL Server. Teraz można symulować zachowanie FOR UPDATE za pomocą transakcji i strategii blokowania. Ale to może być coś więcej niż to, czego szukasz.

+0

Historycznie, mogła występować różnica w zachowaniu (gdy aktualizacje blokowały całą tabelę) - ale nie w ostatniej dekadzie (i całkiem możliwe, że nie od wprowadzenia FOR UPDATE). – JulesLt

2

Oracle:

sesji 1

create table t (id number); 
alter table t add constraint pk primary key(id); 

SELECT * 
FROM t 
WHERE id = 1 
FOR UPDATE; 
-- 0 rows returned 
-- this creates row level lock on table, preventing others from locking table in exclusive mode 

sesji 2

SELECT * 
FROM t 
FOR UPDATE; 
-- 0 rows returned 
-- there are no problems with locking here 

rollback; -- releases lock 


INSERT INTO t 
VALUES (1); 
-- 1 row inserted without problems 
+0

Nie sądzę, że 'SELECT FOR UPDATE' zapobiega' INSERT' w każdej sesji, po prostu 'UPDATE'. –

6

MySQL

SELECT ... FOR UPDATE z aktualizacją

Korzystanie z transakcji InnoDB (auto-commit wyłączony), A SELECT ... FOR UPDATE pozwala jedną sesję, aby tymczasowo zablokować konkretny rekord (lub rekordów) tak, że żadna inna sesja może aktualizować to.Następnie w ramach tej samej transakcji sesja może faktycznie wykonać UPDATE na tym samym rekordzie i zatwierdzić lub wycofać transakcję. Umożliwiłoby to zablokowanie zapisu, aby żadna inna sesja nie mogła go zaktualizować, podczas gdy być może wykonasz inną logikę biznesową.

Dokonuje się tego za pomocą blokady. InnoDB wykorzystuje indeksy do blokowania rekordów, więc zablokowanie istniejącego rekordu wydaje się łatwe - wystarczy zablokować indeks dla tego rekordu.

SELECT ... FOR UPDATE z INSERT

Jednak, aby korzystać SELECT ... FOR UPDATE z INSERT, jak można zablokować indeksu dla rekordu, który jeszcze nie istnieje? Jeśli używasz domyślnego poziomu izolacji REPEATABLE READ, InnoDB będzie również wykorzystywać blokady luki. Tak długo, jak znasz id (lub nawet zakres identyfikatorów), aby zablokować, InnoDB może zablokować lukę, aby żaden inny rekord nie mógł zostać wstawiony do tej luki, dopóki nie skończymy z tym.

Jeśli kolumna id były kolumny automatycznego przyrostu, następnie SELECT ... FOR UPDATE z INSERT INTO byłoby problematyczne, ponieważ nie wiedzą, co nowy id dopiero go włożona. Ponieważ jednak znasz numer id, który chcesz wstawić, zadziała SELECT ... FOR UPDATE z INSERT.

CAVEAT

na domyślnym poziomie izolacji, SELECT ... FOR UPDATE na nieistniejącego rekordu robi nie blokowe innych transakcji. Tak więc, jeśli dwie transakcje dokonają zarówno SELECT ... FOR UPDATE na tym samym nieistniejącym rekordzie indeksu, oba otrzymają blokadę, a żadna transakcja nie będzie w stanie zaktualizować rekordu. W rzeczywistości, jeśli obaj spróbują, zostanie wykryty zakleszczenie, a jeden zostanie wycofany.

Dlatego też, jeśli nie chcesz do czynienia z impasu, to może po prostu wykonaj następujące czynności:

INSERT INTO ...

rozpocząć transakcję i wykonać INSERT. Wykonaj swoją logikę biznesową i zatwierdz lub wycofaj transakcję. Jak tylko wykonasz INSERT na nieistniejącym indeksie rekordu na pierwszym transaku, wszystkie inne transakcje zostaną zablokowane, jeśli spróbują uzyskać rekord o tym samym unikalnym indeksie. Jeśli druga transakcja spróbuje wstawić rekord z tym samym indeksem po tym, jak pierwsza transakcja zatwierdzi wstawkę, otrzyma błąd "duplikatu klucza". Postępuj odpowiednio.

SELECT ... LOCK IN SHARE MODE

Jeżeli wybierzesz z LOCK IN SHARE MODE przed INSERT, jeśli poprzednia transakcja została włożona tego rekordu, ale jeszcze nie popełnione, SELECT ... LOCK IN SHARE MODE będzie blokować aż do poprzedniej transakcji zostało zakończone.

Tak, aby zmniejszyć ryzyko zduplikowane kluczowych błędów, zwłaszcza jeśli posiadają zamki na chwilę podczas wykonywania logiki biznesowej przed ich zatwierdzania lub wycofywania ich z powrotem:

  1. SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE
  2. Jeśli żadne rekordy zwrócone, a następnie
  3. INSERT INTO FooBar (foo, bar) VALUES (?, ?)
+0

Ogólnie prawda, ale WYBIERZ ... DLA AKTUALIZACJI z WSTAWEM jest jednak bezużyteczne IMHO, przynajmniej w MySQL. Proszę zajrzeć [tutaj] (http://stackoverflow.com/a/31184293/3239842), aby uzyskać wyjaśnienie. – Binarus

Powiązane problemy