Database
 sql >> Baza danych >  >> RDS >> Database

Poprawka błędu 2008 R2, która łamie RCSI

Jedna z poprawek zawartych w zbiorczej aktualizacji 11 dla dodatku Service Pack 2 dla programu SQL Server 2008 R2 rozwiązuje „nieprawidłowe zakleszczenie”, które może wystąpić w określonym scenariuszu (opisanym w dalszej części tego artykułu). Niestety, poprawka wprowadza nowy błąd, w którym zapytania SELECT w ramach RCSI (odczyt izolacji migawki popełnionej) zaczynają przyjmować blokady na poziomie tabeli. W konsekwencji może wystąpić zwiększone blokowanie (i potencjalnie zakleszczenie) dla zapytań RCSI po zastosowaniu 2008 R2 SP2 CU11 (lub nowszego).

Będzie to niemile widziana niespodzianka dla każdego, kto jest przyzwyczajony do tego, że czytelnicy nie blokują pisarzy (i vice versa) podczas korzystania z RCSI. W chwili pisania tego tekstu nie ma naprawy błędu RCSI. W rzeczywistości element Connect stworzony przez Eugene'a Karpovicha w celu zgłoszenia problemu został zamknięty jako „nie da się naprawić”, chociaż rozumiem, że ta decyzja jest obecnie w trakcie sprawdzania.

Zwykle ten problem może nie być tak dużym problemem, ponieważ aktualizacje zbiorcze nie są zazwyczaj stosowane tak powszechnie, jak pełne dodatki Service Pack. Jednak firma Microsoft ogłosiła niedawno, że pojawi się ostateczny dodatek Service Pack 3 dla SQL Server 2008 R2. Ten dodatek Service Pack będzie prostym zestawieniem istniejących aktualizacji zbiorczych dodatku SP2 (włącznie z CU13), ale bez nowych poprawek. Wynik tego wszystkiego jest taki, że o ile w międzyczasie coś się nie zmieni, użytkownicy stosujący SP3 nagle zaczną być dotknięci błędem RCSI wprowadzonym w CU11.

edytuj:Tuż przed opublikowaniem tego artykułu firma Microsoft potwierdziła, że ​​ta regresja zostanie naprawiona w dodatku SP3.

Ten sam błąd „nieprawidłowego zakleszczenia” (którego poprawka wprowadza nowy błąd) został również naprawiony w aktualizacji zbiorczej 8 dla dodatku Service Pack 1 dla programu SQL Server 2012 zgodnie z opisem w KB2923460. Poprawka dla SQL Server 2012 jest inna i nie przedstaw nowy problem RCSI.

SQL Server 2014 nigdy nie był dotknięty żadnym problemem, o ile wiem. Z pewnością nie ma dokumentacji wskazującej inaczej, a testy, które przeprowadziłem na 2014 RTM, CU1 i CU2 nie odtwarzają żadnego błędu.

Błąd 2008 R2 RCSI

Kwerenda SELECT działająca w ramach RCSI zwykle przyjmuje tylko blokadę stabilności schematu (Sch-S), która jest zgodna ze wszystkimi innymi blokadami z wyjątkiem blokady modyfikacji schematu (Sch-M). Gdy CU11 (lub nowszy) jest stosowany do wystąpienia programu SQL Server 2008 R2, te kwerendy zaczynają brać blokadę na poziomie tabeli (Tab-IS). Do wykazania różnic w zachowaniach można użyć następującego skryptu testowego:

USE master;
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET NOCOUNT ON;
GO
CREATE DATABASE RCSI;
GO
ALTER DATABASE RCSI
SET READ_COMMITTED_SNAPSHOT ON;
GO
ALTER DATABASE RCSI
SET ALLOW_SNAPSHOT_ISOLATION OFF;
GO
USE RCSI;
GO
CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL, 
    col1 integer NOT NULL,
 
    CONSTRAINT PK_Test
    PRIMARY KEY CLUSTERED (id)
);
GO
INSERT dbo.Test 
    (col1) 
VALUES 
    (1), (2), (3), (4);
GO
-- Show locks
DBCC TRACEON (1200, 3604, -1) WITH NO_INFOMSGS;
SELECT * FROM dbo.Test;
DBCC TRACEOFF (1200, 3604, -1) WITH NO_INFOMSGS;
GO
ALTER DATABASE RCSI
SET SINGLE_USER
WITH ROLLBACK IMMEDIATE;
 
USE master;
 
DROP DATABASE RCSI;

Po uruchomieniu na instancji SQL Server 2008 R2 bez błędu, dane wyjściowe debugowania pokazują pojedynczą blokadę Sch-S pobraną dla instrukcji test zgodnie z oczekiwaniami:

Proces pozyskiwania blokady Sch-S na OBJECT:7:2105058535:0 wynik:OK
Proces zwalniający blokadę na OBJECT:7:2105058535:0

W przypadku uruchomienia na SQL Server 2008 R2 w wersji 10.50.4302 (lub nowszej) dane wyjściowe są podobne do:

Pozyskiwanie procesu IS blokada na OBJECT:7:2105058535:0 wynik:OK
Proces zwalniający blokadę na OBJECT:7:2105058535:0

Zauważ, że blokada Sch-S została zastąpiona blokadą Tab-IS.

Implikacje i łagodzenie

Blokada współdzielona (IS) jest nadal bardzo kompatybilną blokadą, ale nie jest tak przyjazna dla współbieżności jak Sch-S. Macierz kompatybilności blokad pokazuje, że blokada IS jest w konflikcie z:

  • Sch-M (modyfikacja schematu) – wg Sch-S
  • BU (aktualizacja zbiorcza)
  • X (wyłącznie)

Niezgodność z blokadami na wyłączność (X) oznacza, że ​​odczyt w ramach RCSI zostanie zablokowany, jeśli współbieżny proces ma blokadę na wyłączność na tym samym zasobie. Podobnie, program zapisujący, który potrzebuje blokady na wyłączność, zablokuje się, jeśli współbieżny czytnik RCSI posiada blokadę IS. Wyjątkowe blokady są uzyskiwane za każdym razem, gdy dane są modyfikowane i utrzymywane do końca transakcji, więc skutkiem błędu jest to, że czytniki pod kontrolą RCSI będą blokowane przez współbieżne programy piszące (i odwrotnie), gdy nie byli przed zastosowaniem CU11.

Istotnym czynnikiem łagodzącym jest to, że błąd powoduje tylko poziom tabeli Wspólna blokada do zabrania. Współbieżny program piszący, który potrzebuje poziomu tabeli blokada na wyłączność spowoduje blokowanie (i potencjalnie zakleszczenie). Jednak współbieżne programy piszące, które wymagają wyłącznych blokad tylko na niższym poziomie (np. wiersza, strony lub partycji), nie spowodować blokadę lub zakleszczenie. Na poziomie tabeli ci autorzy uzyskają tylko blokadę na wyłączność (IX), która jest zgodna z Tab-IS. Blokady na wyłączność podejmowane na niższych poziomach szczegółowości nie spowodują konfliktu.

W większości systemów blokady na poziomie tabeli (Tab-X) będą stosunkowo rzadkie. O ile wyraźnie nie zażądano tego za pomocą podpowiedzi TABLOCKX, niektóre możliwe przyczyny blokady Tab-X to:

  • Zablokuj eskalację z niższej szczegółowości
  • Korzystanie z funkcji SERIALIZABLE bez indeksu pomocniczego dla zamków z zakresem klawiszy

Obejściem technicznym jest dodanie (nadmiarowej) wskazówki do tabeli WITH (READCOMMITTED) do każdej tabeli w każdym zapytaniu uruchamianym w ramach RCSI. Zdarza się, że omija to błąd, więc bierze się tylko blokadę Sch-S, ale nie jest to praktyczna propozycja.

Pomimo tych ograniczeń, przyjmowanie Tab-IS do zapytania tylko do odczytu w ramach RCSI jest nadal nieprawidłowym zachowaniem. Mam nadzieję, że można to naprawić w SQL Server 2008 R2 przed wydaniem dodatku Service Pack 3.

Błąd „Niepoprawny impas”

Jak wspomniano wcześniej, błąd RCSI został wprowadzony jako efekt uboczny poprawki błędu „nieprawidłowego zakleszczenia”. Ten wcześniejszy problem jest udokumentowany dla programu SQL Server 2008 R2 w KB2929464 i dla programu SQL Server 2012 w KB2923460. Żaden dokument nie jest modelem przejrzystości (ani dokładności), ale podstawowy problem jest dość interesujący, więc chcę poświęcić trochę czasu na zapoznanie się z nim tutaj.

Zasadniczo impas występuje, gdy:

  • Trzy lub więcej równoczesnych transakcji odczytanych z tej samej tabeli
  • Wskazówki UPDLOCK i TABLOCK są używane we wszystkich trzech przypadkach
  • Ustawienie bazy danych READ_COMMITTED_SNAPSHOT jest włączone

Należy zauważyć, że nie ma znaczenia, na jakim poziomie izolacji działają transakcje. Aby odtworzyć błąd, najpierw uruchom poniższy skrypt instalacyjny:

USE master;
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
GO
CREATE DATABASE IncorrectDeadlock;
GO
ALTER DATABASE IncorrectDeadlock 
SET READ_COMMITTED_SNAPSHOT ON;
GO
USE IncorrectDeadlock;
GO
CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL, 
    col1 integer NOT NULL,
 
    CONSTRAINT PK_Test
    PRIMARY KEY CLUSTERED (id)
);
GO
INSERT dbo.Test 
    (col1) 
VALUES 
    (1);

Następnie uruchom następujący skrypt w trzech osobnych połączeniach (zwróć uwagę, że transakcja pozostaje otwarta):

USE IncorrectDeadlock;
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
GO
BEGIN TRANSACTION;
SELECT
    T.id,
    T.col1
FROM dbo.Test AS T
    WITH (UPDLOCK, TABLOCK);

W tym momencie pierwsza sesja zwróci zestaw wyników, a dwie pozostałe zostaną zablokowane. „Nieprawidłowe zakleszczenie” pojawia się, gdy pierwsza sesja kończy transakcję (zatwierdzanie lub wycofywanie). W takim przypadku jedna z dwóch pozostałych sesji zgłosi zakleszczenie:

Zakleszczenie występuje, ponieważ dwie wcześniej zablokowane sesje zawierają Tab-IX (wyłącznie na poziomie tabeli) i obie chcą przekonwertować blokadę na Tab-X (wyłącznie na poziomie tabeli). Tab-IX jest kompatybilny z innym Tab-IX, ale nie Tab-X. Jest to zakleszczenie konwersji (i ironia polega na tym, że UPDLOCK jest często używany w celu uniknięcia zakleszczeń konwersji).

Możesz dowolnie zmieniać poziom izolacji transakcji dla trzech zapytań. Zakleszczenie wystąpi niezależnie od tego, o ile włączone jest RCSI, z tymi samymi blokadami. Po zakończeniu testów usuń testową bazę danych:

USE IncorrectDeadlock;
 
ALTER DATABASE IncorrectDeadlock
SET SINGLE_USER 
WITH ROLLBACK IMMEDIATE;
 
USE master;
 
DROP DATABASE IncorrectDeadlock;

Analiza i wyjaśnienie

Osobiście nie przypominam sobie, abym kiedykolwiek używał razem UPDLOCK i TABLOCK w moim kodzie. Dla mnie ta kombinacja wskazówek wydaje się intuicyjnie dziwna, ponieważ SQL Server nie ma blokady aktualizacji na poziomie tabeli . A więc co to w ogóle oznacza określić jednocześnie wskazówki UPDLOCK i TABLOCK?

Dokumentacja ma to do powiedzenia:

BLOKADA GÓRY

Określa, że ​​blokady aktualizacji mają być podejmowane i utrzymywane do czasu zakończenia transakcji. UPDLOCK przyjmuje blokady aktualizacji dla operacji odczytu tylko na poziomie wiersza lub na poziomie strony. Jeśli UPDLOCK jest połączony z TABLOCK lub blokada na poziomie tabeli zostanie zastosowana z jakiegoś innego powodu, zamiast tego zostanie zastosowana blokada na wyłączność (X).

Sugeruje to, że kombinacja podpowiedzi powinna skutkować jedną wyłączną blokadą stołu. W rzeczywistości to nie jest cała historia:

W SQL Server 2000, połączenie wskazówek UPDLOCK i TABLOCK powoduje, że Tab-S (blokada wspólnej tabeli) jest pobierana, po czym następuje konwersja do Tab-X (wyłączna blokada tabeli) na wszystkich poziomach izolacji z wyjątkiem READ UNCOMMITTED. Ta sekwencja blokad może skutkować zakleszczeniem, w którym zaangażowane są co najmniej trzy sesje:dwie sesje przejmują Tab-S i obie czekają na siebie, aby przekonwertować na Tab-X. Pod READ UNCOMMITTED, SQL Server 2000 przyjmuje Sch-S, a następnie Tab-X, który nie jest podatny na zakleszczenia (po prostu normalne blokowanie).

Od wersji SQL Server 2005 (bez naprawy błędów) podejmowane blokady zależą tylko czy RCSI jest włączony czy nie. Jeśli RCSI jest włączony, wszystkie poziomy izolacji weź Tab-IX, a następnie przekonwertuj na Tab-X. Ta sekwencja powoduje zakleszczenie adresów naprawionych błędów.

Jeśli RCSI nie jest włączone, pasujące poziomy izolacji zachowują się tak, jak w SQL Server 2000 (przyjmując Tab-S, a następnie konwertując na Tab-X). Poziom izolacji migawki (nowość w 2005 r.) przyjmuje Sch-S, a następnie Tab-X. W konsekwencji SI i READ UNCOMMITTED są jedynymi poziomami izolacji, które nie są podatne na to zakleszczenie w scenariuszu UPDLOCK, TABLOCK, gdy RCSI nie jest włączony.

Naprawa impasu

Poprawka zmienia blokady przyjmowane, gdy UPDLOCK i TABLOCK są określone razem, dla wszystkich poziomów izolacji i niezależnie czy RCSI jest włączony czy nie. Po zastosowaniu poprawki UPDLOCK i TABLOCK powodują, że silnik uzyskuje Tab-SIX (poziom tabeli współdzielony z wyłącznością intencji), który jest następnie konwertowany na Tab-X.

Pozwala to uniknąć zakleszczenia, ponieważ Tab-SIX jest niezgodny z innym Tab-SIX. Pamiętaj, że zakleszczenie nastąpiło, gdy dwa procesy trzymały Tab-IX czekając na konwersję do Tab-X. Gdy Tab-IX został zastąpiony przez Tab-SIX, nie jest możliwe, aby obaj jednocześnie trzymali Tab-SIX. Rezultatem jest normalny scenariusz blokowania zamiast impasu.

Ostateczne myśli

Poprawka „nieprawidłowego zakleszczenia” rozwiązuje jeden konkretny scenariusz zakleszczenia, ale nadal nie powoduje zachowania, które wyobrażam sobie, że ludzie określają przewidywane UPDLOCK i TABLOCK. Jeśli SQL Server miałby blokadę Tab-U (aktualizacja na poziomie tabeli), uniemożliwiłoby to współbieżne zmiany w tabeli, ale umożliwiłoby współbieżne czytniki. Wyobrażam sobie, że takie byłyby intencje ludzi korzystających z tych wskazówek razem i widzę, jak może to być przydatne.

Obecna implementacja (gdzie Tab-X jest ostatecznie pobierana zamiast brakującej Tab-U) nie spełnia tego oczekiwania, ponieważ Tab-X zapobiega równoczesnym odczytom (chyba że jest używany poziom izolacji wersjonowania wierszy). Równie dobrze moglibyśmy określić TABLOCKX w wielu przypadkach. Fakt, że poprawka wprowadza również nowy błąd (tylko dla użytkowników SQL Server 2008 R2), jest również niefortunny, zwłaszcza jeśli błąd zostanie uwzględniony w dodatku 2008 R2 SP3.

Należy zauważyć, że poprawka zakleszczenia nie jest udostępniana dla wersji SQL Server starszych niż 2008 R2. Te wersje będą nadal miały złożone zachowanie blokowania dla UPDLOCK i TABLOCK, jak opisano powyżej.

Moje podziękowania dla Eugene'a Karpovicha, który jako pierwszy zwrócił mi uwagę na ten problem w komentarzu do mojego artykułu na temat modyfikacji danych w ramach RCSI.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak uszeregować wiersze w partycji w SQL?

  2. Poziomy zgodności i podstawa szacowania kardynalności

  3. Obciążenie związane ze śledzeniem tworzenia tabeli #temp

  4. Śledzenie synchronicznych aktualizacji statystyk

  5. Kto jest aktywnym biegaczem