Wybierz do aktualizacji za pomocą SQL Server

Używam bazy danych Microsoft SQL Server 2005 z poziomem izolacji READ_COMMITTED i READ_COMMITTED_SNAPSHOT=ON.

Teraz chcę użyć:

SELECT * FROM <tablename> FOR UPDATE

... tak, że inne połączenia bazodanowe blokują się podczas próby uzyskania dostępu do tego samego wiersza "dla aktualizacji".

Próbowałem:

SELECT * FROM <tablename> WITH (updlock) WHERE id=1

...ale to blokuje wszystkie inne połączenia nawet dla wybrania identyfikatora innego niż"1".

Która jest prawidłowa podpowiedź do zrobienia SELECT FOR UPDATE jak wiadomo dla Oracle, DB2, MySql?

Edytuj 2009-10-03:

Są to instrukcje do tworzenia tabeli i indeksu:

CREATE TABLE example ( Id BIGINT NOT NULL, TransactionId BIGINT, 
    Terminal BIGINT, Status SMALLINT );
ALTER TABLE example ADD CONSTRAINT index108 PRIMARY KEY ( Id )
CREATE INDEX I108_FkTerminal ON example ( Terminal )
CREATE INDEX I108_Key ON example ( TransactionId )

Wiele procesów równoległych to robi SELECT:

SELECT * FROM example o WITH (updlock) WHERE o.TransactionId = ?

Edytuj 2009-10-05:

Dla lepszego przeglądu zapisałem wszystkie wypróbowane rozwiązania w poniższej tabeli:

mechanism              | SELECT on different row blocks | SELECT on same row blocks
-----------------------+--------------------------------+--------------------------
ROWLOCK                | no                             | no
updlock, rowlock       | yes                            | yes
xlock,rowlock          | yes                            | yes
repeatableread         | no                             | no
DBCC TRACEON (1211,-1) | yes                            | yes
rowlock,xlock,holdlock | yes                            | yes
updlock,holdlock       | yes                            | yes
UPDLOCK,READPAST       | no                             | no

I'm looking for        | no                             | yes
Author: Bill Paetzke, 2009-09-27

18 answers

Ostatnio miałem problem z blokadą ponieważ SQL Server blokuje więcej niż konieczne (strona). Nie możesz nic przeciwko temu zrobić. Teraz łapiemy impas WYJĄTKÓW... chciałbym mieć wyrocznię.

Edytuj: W międzyczasie używamy izolacji migawek, która rozwiązuje wiele, ale nie wszystkie problemy. Niestety, aby móc korzystać z izolacji migawek, musi to być dozwolone przez serwer bazy danych, co może powodować niepotrzebne problemy w witrynie klientów. Teraz nie jesteśmy tylko wyłapywanie WYJĄTKÓW impasowych (które oczywiście nadal mogą wystąpić), ale także migawanie problemów z współbieżnością w celu powtarzania transakcji z procesów w tle (które nie mogą być powtarzane przez użytkownika). Ale to nadal działa znacznie lepiej niż wcześniej.

 31
Author: Stefan Steinegger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:46:36

Mam podobny problem, chcę zablokować tylko 1 wiersz. O ile wiem, z opcją UPDLOCK, SQLServer blokuje wszystkie wiersze, które musi przeczytać, aby uzyskać wiersz. Tak więc, jeśli nie zdefiniujesz indeksu w celu bezpośredniego dostępu do wiersza, wszystkie poprzedzające wiersze zostaną zablokowane. W twoim przykładzie:

Ponieważ masz tabelę o nazwie TBL z polem id. Chcesz zablokować rząd za pomocą id=10. Musisz zdefiniować indeks dla identyfikatora pola (lub innych pól, które są w tobie zaangażowane Wybierz):

CREATE INDEX TBLINDEX ON TBL ( id )

A następnie Twoje zapytanie, aby zablokować tylko wiersze, które przeczytałeś, to:

SELECT * FROM TBL WITH (UPDLOCK, INDEX(TBLINDEX)) WHERE id=10.

Jeśli nie używasz opcji INDEX(TBLINDEX), SQLSERVER musi odczytać wszystkie wiersze od początku tabeli, aby znaleźć wiersz z id=10, więc te wiersze zostaną zablokowane.

 17
Author: ManuelConde,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-16 10:35:48

Nie można mieć jednocześnie izolacji migawek i blokowania odczytów. Celem izolacji migawek jest zapobieganie blokowaniu odczytów.

 7
Author: Christian Hayter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-30 08:46:38

Try (updlock, rowlock)

 5
Author: BlueMonkMN,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-27 14:59:47

Pełna odpowiedź może zagłębić się w wewnętrzne systemy DBMS. To zależy od tego, jak działa silnik zapytań (który wykonuje plan zapytań wygenerowany przez optymalizator SQL).

Jednakże, jednym z możliwych wyjaśnień (dotyczy przynajmniej niektórych wersji niektórych DBMS - niekoniecznie MS SQL Server) jest to, że w kolumnie ID nie ma indeksu, więc każdy proces próbujący wykonać zapytanie z 'WHERE id = ? ' kończy się wykonaniem sekwencyjnego skanowania tabeli i że sekwencyjne skanowanie powoduje blokadę który zastosował twój proces. Możesz również napotkać problemy, jeśli DBMS domyślnie stosuje blokowanie na poziomie strony; blokowanie jednego wiersza blokuje całą stronę i wszystkie wiersze na tej stronie.

Jest kilka sposobów, aby obalić to jako źródło kłopotów. Spójrz na plan zapytań; zbadaj indeksy; spróbuj wybrać z ID 1000000 zamiast 1 i sprawdź, czy inne procesy są nadal zablokowane.

 5
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-27 15:00:42

Być może uczynienie mvcc stałym mogłoby to rozwiązać (w przeciwieństwie do konkretnych partii tylko: Ustaw migawkę poziomu izolacji transakcji):

ALTER DATABASE yourDbNameHere SET READ_COMMITTED_SNAPSHOT ON;

[edycja: Październik 14]

Po przeczytaniu tego: lepsza współbieżność w Oracle niż SQL Server? i to: http://msdn.microsoft.com/en-us/library/ms175095.aspx

Gdy READ_COMMITTED_SNAPSHOT opcja bazy danych jest ustawiona na, mechanizmy stosowane do obsługi opcji są aktywowane natychmiast. Kiedy ustawianie READ_COMMITTED_SNAPSHOT opcja, tylko połączenie wykonujące dozwolone jest polecenie ALTER DATABASE w bazie danych. Nie może być inne otwarte połączenie w bazie danych dopóki baza danych ALTER nie zostanie ukończona. Na baza danych nie musi być w tryb dla jednego użytkownika.

Doszedłem do wniosku, że aby aktywować MVCC mssql na stałe w danej bazie danych, trzeba ustawić dwa FLAG:

ALTER DATABASE yourDbNameHere SET ALLOW_SNAPSHOT_ISOLATION ON;
ALTER DATABASE yourDbNameHere SET READ_COMMITTED_SNAPSHOT ON;
 5
Author: Michael Buen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:32:20

Ok, pojedynczy select domyślnie używa izolacji transakcji "read Committed", która blokuje i tym samym zatrzymuje zapis do tego zestawu. Możesz zmienić poziom izolacji transakcji za pomocą

Set Transaction Isolation Level { Read Uncommitted | Read Committed | Repeatable Read | Serializable }
Begin Tran
  Select ...
Commit Tran

Są one szczegółowo wyjaśnione w SQL Server BOL

Następnym problemem jest to, że domyślnie SQL Server 2K5 eskaluje blokady, jeśli masz więcej niż ~2500 blokad lub używasz więcej niż 40% 'normalnej' pamięci w transakcji blokady. Eskalacja idzie na stronę, a następnie tabelę lock

Możesz wyłączyć tę eskalację, ustawiając "trace flag" 1211t, zobacz BOL, aby uzyskać więcej informacji

 3
Author: TFD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-03 21:32:15

Zakładam, że nie chcesz, aby żadna inna sesja była w stanie odczytać wiersz podczas uruchamiania tego konkretnego zapytania...

Owinięcie SELECT w transakcję podczas używania podpowiedzi blokującej (XLOCK,READPAST) spowoduje uzyskanie pożądanych wyników. Upewnij się tylko, że inne współbieżne odczyty nie używają WITH (NOLOCK). READPAST pozwala innym sesjom wykonać to samo SELECT, ale w innych wierszach.

BEGIN TRAN
  SELECT *
  FROM <tablename> WITH (XLOCK,READPAST) 
  WHERE RowId = @SomeId

  -- Do SOMETHING

  UPDATE <tablename>
  SET <column>=@somevalue
  WHERE RowId=@SomeId
COMMIT
 2
Author: ewoo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-18 23:31:02

Blokady aplikacji są jednym ze sposobów na zrolowanie własnego zamka z niestandardową szczegółowością, unikając" pomocnej " eskalacji blokady. Zobacz sp_getapplock .

 2
Author: Constantin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-11 00:26:55

Utwórz fałszywą aktualizację, aby wymusić blokadę rowlock.

UPDATE <tablename> (ROWLOCK) SET <somecolumn> = <somecolumn> WHERE id=1
Jeśli to nie blokuje twojego rzędu, Bóg wie, co będzie.

Po Tym "UPDATE " możesz zrobić swoje SELECT (ROWLOCK) i kolejne aktualizacje.

 2
Author: Feu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-14 12:07:43

Spróbuj użyć:

SELECT * FROM <tablename> WITH ROWLOCK XLOCK HOLDLOCK

Powinno to uczynić blokadę wyłączną i utrzymać ją na czas trwania transakcji.

 1
Author: RMorrisey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-27 15:38:52

Zgodnie z Ten artykuł, rozwiązaniem jest użycie podpowiedzi WITH (REPEATABLEREAD).

 1
Author: erikkallen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-30 09:00:37

Wróć do wszystkich zapytań, może masz jakieś zapytanie, które Wybierz bez ROWLOCK/dla podpowiedzi aktualizacji z tej samej tabeli, którą wybrałeś dla aktualizacji.


MSSQL często eskaluje te blokady wierszy do blokad na poziomie strony (nawet blokad na poziomie tabeli, jeśli nie masz indeksu w polu, które pytasz), zobacz to Wyjaśnienie . Ponieważ prosisz o aktualizację, mogę założyć, że potrzebujesz solidności na poziomie transakcji(np. finansowej, inwentaryzacyjnej itp.). Więc porady na tej stronie nie są dotyczy Twojego problemu. To tylko wgląd, dlaczego MSSQL eskaluje blokady .


Jeśli używasz już MSSQL 2005( i up), Są one oparte na MVCC, myślę, że nie powinieneś mieć problemu z blokadą na poziomie wiersza za pomocą podpowiedzi ROWLOCK/UPDLOCK. Ale jeśli używasz już MSSQL 2005 i nowszych, spróbuj sprawdzić niektóre z zapytań, które pytają o tę samą tabelę, którą chcesz zaktualizować, jeśli eskalują blokady, sprawdzając pola na klauzuli WHERE, jeśli mają indeks.


P. S.
Używam PostgreSQL, używa również MVCC have do aktualizacji, nie napotykam tego samego problemu. Eskalacje blokad są tym, co rozwiązuje MVCC, więc byłbym zaskoczony, gdyby MSSQL 2005 nadal eskalował blokady w tabeli z klauzulami WHERE, które nie mają indeksu na swoich polach. Jeśli tak (lock escalation) jest nadal w MSSQL 2005, spróbuj sprawdzić pola gdzie klauzule, jeśli mają indeks.

Zastrzeżenie: moje ostatnie użycie MSSQL jest tylko wersja 2000.

 1
Author: Michael Buen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-30 09:37:31

Musisz poradzić sobie z wyjątkiem w czasie commit i powtórzyć transakcję.

 1
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-08 21:10:01

Pytanie - czy udowodniono, że ten przypadek jest wynikiem eskalacji blokady (tzn. jeśli prześledzisz za pomocą profilera zdarzenia eskalacji blokady, to na pewno to, co się dzieje, powoduje blokadę)? Jeśli tak, istnieje pełne wyjaśnienie i (raczej ekstremalne) obejście poprzez włączenie znacznika śledzenia na poziomie wystąpienia, aby zapobiec eskalacji blokady. Zobacz http://support.microsoft.com/kb/323630 znacznik 1211

Ale to prawdopodobnie będzie miało niezamierzone skutki uboczne.

Jeśli jesteś celowe blokowanie wiersza i utrzymywanie go zablokowanego przez dłuższy czas, a następnie używanie wewnętrznego mechanizmu blokowania transakcji nie jest najlepszą metodą (przynajmniej w SQL Server). Cała optymalizacja w SQL Server jest nastawiona na krótkie transakcje - wejdź, dokonaj aktualizacji, wyjdź. To jest powód eskalacji blokady.

Więc jeśli intencją jest" sprawdzenie " wiersza przez dłuższy czas, zamiast blokowania transakcji najlepiej użyć kolumny z wartościami i prosta instrukcja ol ' update, aby oznaczyć wiersze jako zablokowane lub nie.

 1
Author: onupdatecascade,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-19 00:01:54

Rozwiązałem problem rowlock w zupełnie inny sposób. Zdałem sobie sprawę, że sql server nie był w stanie zarządzać taką blokadą w satysfakcjonujący sposób. Zdecydowałem się rozwiązać to z programowego punktu widzenia za pomocą mutex... waitForLock... / align = "left" / ..

 1
Author: jessn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-04 20:48:26

Próbowałeś READPAST?

Używałem UPDLOCK i READPAST razem, gdy traktowałem tabelę jak kolejkę.

 0
Author: Gratzy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-02 22:18:21

Może spróbujesz najpierw zrobić prostą aktualizację tego wiersza (bez zmieniania żadnych danych)? Następnie możesz kontynuować wiersz, jak w został wybrany do aktualizacji.

UPDATE dbo.Customer SET FieldForLock = FieldForLock WHERE CustomerID = @CustomerID
/* do whatever you want */

Edit : należy zawinąć w transakcję oczywiście

Edit 2 : innym rozwiązaniem jest użycie SERIALIZOWALNEGO poziomu izolacji

 0
Author: Vladimir,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-23 17:44:43