Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Po zakleszczeniu jednej transakcji w różnych wersjach programu SQL Server

Jednym z mniej powszechnych zakleszczeń jest taki, w którym występuje jeden użytkownik, który blokuje się na jakimś zasobie systemowym. Ostatnio natknąłem się na utworzenie typu aliasu, a następnie zadeklarowanie zmiennej tego typu w tej samej transakcji. Wyobraź sobie, że próbujesz uruchomić test jednostkowy lub test przed wdrożeniem, sprawdzić błędy i cofnąć w każdym przypadku, aby nie pozostawić żadnego śladu po tym, co zrobiłeś. Wzór może wyglądać tak:

BEGIN TRANSACTION;
GO
CREATE TYPE EmailAddress FROM VARCHAR(320);
GO
DECLARE @x TABLE (e EmailAddress);
GO
ROLLBACK TRANSACTION;

Lub, co bardziej prawdopodobne, trochę bardziej złożone:

BEGIN TRANSACTION;
GO
CREATE TYPE EmailAddress FROM VARCHAR(320);
GO
CREATE PROCEDURE dbo.foo 
  @param EmailAddress 
AS
BEGIN 
  SET NOCOUNT ON;
  DECLARE @x TABLE (e EmailAddress);
  INSERT @x SELECT @param;
END
GO
DECLARE @x EmailAddress;
SET @x = N'whatever';
EXEC dbo.foo @param = N'whatever';
GO
ROLLBACK TRANSACTION;

Pierwszym miejscem, w którym wypróbowałem ten kod, był SQL Server 2012, a oba przykłady nie powiodły się z następującym błędem:

Msg 1205, poziom 13, stan 55, wiersz 14
Transakcja (identyfikator procesu 57) została zablokowana w zasobach blokady z innym procesem i została wybrana jako ofiara zakleszczenia. Ponownie uruchom transakcję.

A z wykresu impasu nie można się wiele nauczyć:

Cofając się o kilka lat, przypominam sobie, kiedy po raz pierwszy dowiedziałem się o typach aliasów, w SQL Server 2000 (kiedy nazywano je typami danych definiowanymi przez użytkownika). W tamtym czasie ten impas, na który natknąłem się ostatnio, nie miałby miejsca (ale przynajmniej częściowo dlatego, że nie można było zadeklarować zmiennej tabeli z typem aliasu – zobacz tutaj i tutaj). Uruchomiłem następujący kod na SQL Server 2000 RTM (8.0.194) i SQL Server 2000 SP4 (8.0.2039) i działał dobrze:

BEGIN TRANSACTION;
GO
EXEC sp_addtype @typename = N'EmailAddress', @phystype = N'VARCHAR(320)';
GO
CREATE PROCEDURE dbo.foo 
  @param EmailAddress 
AS
BEGIN 
  SET NOCOUNT ON;
  SELECT @param;
END
GO
EXEC dbo.foo @param = N'whatever';
GO
DECLARE @x EmailAddress;
SET @x = N'whatever';
EXEC dbo.foo @param = @x;
GO
ROLLBACK TRANSACTION;

Oczywiście ten scenariusz nie był wtedy zbyt rozpowszechniony, ponieważ w końcu niewiele osób używało typów aliasów. Chociaż mogą sprawić, że Twoje metadane będą bardziej samodokumentujące i podobne do definicji danych, są one prawdziwym problemem, jeśli kiedykolwiek zechcesz je zmienić, co może być tematem na inny post.

Pojawił się SQL Server 2005 i wprowadził nową składnię DDL do tworzenia typów aliasów:CREATE TYPE . To tak naprawdę nie rozwiązało problemu ze zmianą typów, po prostu sprawiło, że składnia była trochę czystsza. W RTM wszystkie powyższe próbki kodu działały dobrze bez zakleszczeń. Jednak w SP4 wszyscy utknęliby w martwym punkcie. Dlatego gdzieś pomiędzy RTM a SP4 zmieniono wewnętrzną obsługę transakcji, które obejmowały zmienne tabel przy użyciu typów aliasów.

Przewiń o kilka lat do SQL Server 2008, gdzie dodano parametry wyceniane w tabeli (zobacz tutaj dobry przypadek użycia). To sprawiło, że korzystanie z tych typów stało się znacznie bardziej rozpowszechnione i wprowadziło inny przypadek, w którym transakcja, która próbowała utworzyć i użyć takiego typu, byłaby zakleszczona:

BEGIN TRANSACTION;
GO
CREATE TYPE dbo.Items AS TABLE(Item INT);
GO
DECLARE @r dbo.Items;
GO
ROLLBACK TRANSACTION;

Sprawdziłem Connect i znalazłem kilka powiązanych elementów, z których jeden twierdził, że ten problem został naprawiony w SQL Server 2008 SP2 i 2008 R2 SP1:

Połącz #365876 :Zakleszczenie występuje podczas tworzenia typu danych zdefiniowanego przez użytkownika i obiektów, które go używają

W rzeczywistości odnosiło się to do następującego scenariusza, w którym zwykłe utworzenie procedury składowanej, która odwołuje się do typu w zmiennej tabeli, spowodowałoby zakleszczenie w SQL Server 2008 RTM (10.0.1600) i SQL Server 2008 R2 RTM (10.50.1600):

BEGIN TRANSACTION;
GO
CREATE TYPE EmailAddress FROM VARCHAR(320);
GO
CREATE PROCEDURE dbo.foo 
  @param EmailAddress 
AS
BEGIN 
  SET NOCOUNT ON;
  DECLARE @x TABLE (e EmailAddress);
  INSERT @x SELECT @param;
END
GO
ROLLBACK TRANSACTION;

Jednak nie powoduje to zakleszczenia w dodatku SP3 dla programu SQL Server 2008 (10.0.5846) lub 2008 R2 z dodatkiem SP2 (10.50.4295). Dlatego wierzę w komentarze dotyczące elementu Connect – że ta część błędu została naprawiona w 2008 SP2 i 2008 R2 SP1 i nigdy nie stanowiła problemu w nowszych wersjach.

Ale to nadal wyklucza możliwość poddania typu aliasu jakimkolwiek prawdziwym testom. Więc moje testy jednostkowe powiodą się tak długo, jak długo chciałem tylko przetestować, czy mogę stworzyć procedurę – zapomnij o deklarowaniu typu jako zmiennej lokalnej lub jako kolumny w lokalnej zmiennej tabeli.

Jedynym sposobem rozwiązania tego problemu jest utworzenie typu tabeli przed rozpoczęciem transakcji i jawne porzucenie go później (lub w inny sposób rozbicie go na wiele transakcji). Może to być niezwykle uciążliwe, a nawet niemożliwe, aby często zautomatyzowane struktury testowe i narzędzia całkowicie zmieniały sposób ich działania, aby uwzględnić to ograniczenie.

Postanowiłem więc przeprowadzić kilka testów w początkowych i najnowszych kompilacjach wszystkich głównych wersji:SQL Server 2005 RTM, 2005 SP4, 2008 RTM, 2008 SP3, 2008 R2 RTM, 2008 R2 SP2, 2012 RTM, 2012 SP1, i 2014 CTP2 (i tak, mam je wszystkie zainstalowane). Przejrzałem kilka elementów Connect i różne komentarze, które sprawiły, że zacząłem się zastanawiać, które przypadki użycia są obsługiwane i gdzie, i poczułem dziwną potrzebę sprawdzenia, które aspekty tego problemu zostały faktycznie naprawione. Przetestowałem różne potencjalne scenariusze zakleszczenia obejmujące typy aliasów, zmienne tabel i parametry wyceniane w tabeli względem wszystkich tych kompilacji; kod wygląda następująco:

/* 
  alias type - declare in local table variable 
  always deadlocks on 2005 SP4 -> 2014, except in 2005 RTM
*/
 
BEGIN TRANSACTION;
GO
CREATE TYPE EmailAddress FROM VARCHAR(320)
GO
DECLARE @r TABLE(e EmailAddress);
GO
ROLLBACK TRANSACTION;
 
 
/* 
  alias type - create procedure with param & table var 
  sometimes deadlocks - 2005 SP4, 2008 RTM & SP1, 2008 R2 RTM
*/
 
BEGIN TRANSACTION;
GO
CREATE TYPE EmailAddress FROM VARCHAR(320);
GO
CREATE PROCEDURE dbo.foo 
  @param EmailAddress 
AS
BEGIN 
  SET NOCOUNT ON;
  DECLARE @x TABLE (e EmailAddress);
  INSERT @x SELECT @param;
END
GO
ROLLBACK TRANSACTION;
 
 
/* 
  alias type - create procedure, declare & exec 
  always deadlocks on 2005 SP4 -> 2014, except on 2005 RTM
*/
 
BEGIN TRANSACTION;
GO
CREATE TYPE EmailAddress FROM VARCHAR(320);
GO
CREATE PROCEDURE dbo.foo 
  @param EmailAddress 
AS
BEGIN 
  SET NOCOUNT ON;
  DECLARE @x TABLE (e EmailAddress);
  INSERT @x SELECT @param;
END
GO
DECLARE @x EmailAddress;
SET @x = N'whatever';
EXEC dbo.foo @param = N'whatever';
GO
ROLLBACK TRANSACTION;
 
 
/* obviously did not run these on SQL Server 2005 builds */
 
/* 
  table type - create & declare local variable 
  always deadlocks on 2008 -> 2014
*/
 
BEGIN TRANSACTION;
GO
CREATE TYPE dbo.Items AS TABLE(Item INT);
GO
DECLARE @r dbo.Items;
GO
ROLLBACK TRANSACTION;
 
 
/* 
  table type - create procedure with param and SELECT 
  never deadlocks on 2008 -> 2014
*/
 
BEGIN TRANSACTION;
GO
CREATE TYPE dbo.Items AS TABLE(Item INT);
GO
CREATE PROCEDURE dbo.foo 
  @param dbo.Items READONLY
AS
BEGIN 
  SET NOCOUNT ON;
  SELECT Item FROM @param;
END
GO
ROLLBACK TRANSACTION;
 
 
/* 
  table type - create procedure, declare & exec 
  always deadlocks on 2008 -> 2014
*/
 
BEGIN TRANSACTION;
GO
CREATE TYPE dbo.Items AS TABLE(Item INT);
GO
CREATE PROCEDURE dbo.foo 
  @param dbo.Items READONLY
AS
BEGIN 
  SET NOCOUNT ON;
  SELECT Item FROM @param;
END
GO
DECLARE @x dbo.Items;
EXEC dbo.foo @param = @x;
GO
ROLLBACK TRANSACTION;

Wyniki odzwierciedlają moją powyższą historię:SQL Server 2005 RTM nie zablokował się w żadnym ze scenariuszy, ale do czasu wprowadzenia dodatku SP4 wszystkie one się zacięły. Zostało to poprawione dla scenariusza „Utwórz typ i utwórz procedurę”, ale żaden z pozostałych w wersjach 2008 SP2 i 2008 R2 SP1. Oto tabela przedstawiająca wszystkie wyniki:

Wersja / kompilacja serwera SQL
SQL 2005 SQL 2008 SQL 2008 R2 SQL 2012 SQL 2014
RTM
9.0.1399
SP4
9.0.5324
RTM
10.0.1600
SP3
10.0.5846
RTM
10.50.1600
SP2
10.50.4295
RTM
11.0.2100
SP1
11.0.3381
CTP2
12.0.1524
Typ aliasu deklaruj w tabeli var
utwórz procedurę
utwórz i wykonaj proc
Typ tabeli deklaruj lokalną odmianę Nie dotyczy
utwórz procedurę
utwórz i wykonaj proc

Wniosek

Morał tej historii jest taki, że nadal nie ma rozwiązania dla opisanego powyżej przypadku użycia, w którym chcesz utworzyć typ tabeli, utworzyć procedurę lub funkcję korzystającą z tego typu, zadeklarować typ, przetestować moduł i wykonać rzut wszystko z powrotem. W każdym razie, oto inne elementy Connect, na które możesz się przyjrzeć; miejmy nadzieję, że będziesz mógł na nie zagłosować i zostawić komentarze opisujące, w jaki sposób ten impas wpływa bezpośrednio na Twoją firmę:

  • Połącz #581193 :Utworzenie typu tabeli i użycie go w tej samej transakcji powoduje zakleszczenie
  • Połącz #800919:Problem w tworzeniu funkcji z typem zwracanym wartości tabeli w transakcji o typie zdefiniowanym przez użytkownika w tabeli, która jest tworzona w tym samym zakresie transakcji
  • Połącz #804365 :Zakleszczenie występuje, gdy typ tabeli zdefiniowany przez użytkownika jest tworzony i używany w jednej transakcji

    Spodziewam się w niedalekiej przyszłości pewnych wyjaśnień do tych elementów Connect, chociaż nie wiem dokładnie, kiedy zostaną przeforsowane.


    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 wstawić bajt [] do kolumny VARBINARY SQL Server?

    2. SQL Server:maksymalna liczba wierszy w tabeli

    3. Jak podzielić ciąg po określonym znaku w SQL Server i zaktualizować tę wartość do określonej kolumny?

    4. 3 sposoby wyodrębnienia roku z daty w SQL Server (T-SQL)

    5. Zalety wydajności SQL Server 2016 Enterprise Edition