Einer der weniger häufigen Deadlocks ist ein einzelner Benutzer, der sich selbst auf einer Systemressource blockiert. Eines, auf das ich kürzlich gestoßen bin, ist das Erstellen eines Aliastyps und das anschließende Deklarieren einer Variablen dieses Typs innerhalb derselben Transaktion. Stellen Sie sich vor, Sie versuchen, einen Unit-Test oder Pre-Deployment-Test durchzuführen, auf Fehler zu prüfen und in jedem Fall ein Rollback durchzuführen, damit Sie keine Spuren von dem hinterlassen, was Sie getan haben. Das Muster könnte so aussehen:
BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320); GO DECLARE @x TABLE (e EmailAddress); GO ROLLBACK TRANSACTION;
Oder, wahrscheinlicher, etwas komplexer:
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;
Der erste Ort, an dem ich diesen Code ausprobiert habe, war SQL Server 2012, und beide Beispiele sind mit dem folgenden Fehler fehlgeschlagen:
Msg 1205, Level 13, State 55, Line 14Transaktion (Prozess-ID 57) war bei Sperrressourcen mit einem anderen Prozess blockiert und wurde als Deadlock-Opfer ausgewählt. Führen Sie die Transaktion erneut aus.
Und es gibt überhaupt nicht viel aus dem Deadlock-Diagramm zu lernen:
Ich trete ein paar Jahre zurück und erinnere mich, als ich das erste Mal etwas über Alias-Typen in SQL Server 2000 hörte (als sie benutzerdefinierte Datentypen hießen). Damals trat dieser Deadlock, auf den ich kürzlich gestoßen bin, nicht auf (aber das liegt zumindest teilweise daran, dass man eine Tabellenvariable nicht mit einem Alias-Typ deklarieren konnte – siehe hier und hier). Ich habe den folgenden Code auf SQL Server 2000 RTM (8.0.194) und SQL Server 2000 SP4 (8.0.2039) ausgeführt, und er lief einwandfrei:
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;
Natürlich war dieses Szenario damals nicht sehr weit verbreitet, weil schließlich nicht viele Leute überhaupt Alias-Typen verwendeten. Während sie Ihre Metadaten möglicherweise selbstdokumentierender und datendefinitionsähnlicher machen, sind sie ein königlicher Schmerz, wenn Sie sie jemals ändern möchten, was möglicherweise ein Thema für einen anderen Beitrag ist.
SQL Server 2005 kam und führte eine neue DDL-Syntax zum Erstellen von Aliastypen ein:CREATE TYPE
. Das hat das Problem mit dem Ändern der Typen nicht wirklich gelöst, es hat nur die Syntax ein wenig sauberer gemacht. In RTM funktionierten alle oben genannten Codebeispiele ohne Deadlocks einwandfrei. In SP4 kam es jedoch zu einem Deadlock. Daher haben sie irgendwo zwischen RTM und SP4 die interne Behandlung von Transaktionen geändert, die Tabellenvariablen mit Alias-Typen beinhalteten.
Springen Sie ein paar Jahre vor zu SQL Server 2008, wo Tabellenwertparameter hinzugefügt wurden (siehe hier einen guten Anwendungsfall). Dadurch wurde die Verwendung dieser Typen viel häufiger und es wurde ein weiterer Fall eingeführt, in dem eine Transaktion, die versuchte, einen solchen Typ zu erstellen und zu verwenden, blockiert wurde:
BEGIN TRANSACTION; GO CREATE TYPE dbo.Items AS TABLE(Item INT); GO DECLARE @r dbo.Items; GO ROLLBACK TRANSACTION;
Ich habe Connect überprüft und mehrere verwandte Elemente gefunden, von denen eines behauptet, dass dieses Problem in SQL Server 2008 SP2 und 2008 R2 SP1 behoben wurde:
Connect #365876 :Deadlock tritt auf, wenn ein benutzerdefinierter Datentyp und Objekte erstellt werden, die ihn verwenden
Worauf sich dies tatsächlich bezog, war das folgende Szenario, in dem das einfache Erstellen einer gespeicherten Prozedur, die auf den Typ in einer Tabellenvariablen verweist, in SQL Server 2008 RTM (10.0.1600) und SQL Server 2008 R2 RTM (10.50.1600) zu einem Deadlock führen würde:
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;
Dies führt jedoch nicht zu einem Deadlock in SQL Server 2008 SP3 (10.0.5846) oder 2008 R2 SP2 (10.50.4295). Daher neige ich dazu, den Kommentaren zum Connect-Element zu glauben – dass dieser Teil des Fehlers in 2008 SP2 und 2008 R2 SP1 behoben wurde und in moderneren Versionen nie ein Problem war.
Aber dies lässt immer noch die Möglichkeit aus, den Alias-Typ tatsächlich irgendeiner Art von echten Tests zu unterziehen. Meine Komponententests waren also erfolgreich, solange ich nur testen wollte, ob ich die Prozedur erstellen konnte – vergessen Sie, den Typ als lokale Variable oder als Spalte in einer lokalen Tabellenvariablen zu deklarieren.
Die einzige Möglichkeit, dies zu beheben, besteht darin, den Tabellentyp vor dem Starten der Transaktion zu erstellen und ihn danach explizit zu löschen (oder ihn anderweitig in mehrere Transaktionen aufzuteilen). Dies könnte äußerst umständlich oder sogar unmöglich sein, wenn häufig automatisierte Test-Frameworks und -Harnesse ihre Funktionsweise vollständig ändern, um diese Einschränkung zu berücksichtigen.
Also beschloss ich, einige Tests in den ersten und neuesten Builds aller Hauptversionen zu durchlaufen:SQL Server 2005 RTM, 2005 SP4, 2008 RTM, 2008 SP3, 2008 R2 RTM, 2008 R2 SP2, 2012 RTM, 2012 SP1, und 2014 CTP2 (und ja, ich habe sie alle installiert). Ich hatte mehrere Connect-Elemente und verschiedene Kommentare überprüft, bei denen ich mich fragte, welche Anwendungsfälle unterstützt wurden und wo, und ich hatte einen seltsamen Zwang, herauszufinden, welche Aspekte dieses Problems tatsächlich behoben worden waren. Ich habe verschiedene potenzielle Deadlock-Szenarien mit Aliastypen, Tabellenvariablen und Tabellenwertparametern für alle diese Builds getestet. Der Code lautet wie folgt:
/* 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;
Und die Ergebnisse spiegeln meine obige Geschichte wider:SQL Server 2005 RTM führte bei keinem der Szenarien zu einem Deadlock, aber als SP4 eingeführt wurde, waren alle blockiert. Dies wurde in 2008 SP2 und 2008 R2 SP1 für das Szenario „Erstellen eines Typs und Erstellen einer Prozedur“ korrigiert, aber nicht für die anderen. Hier ist eine Tabelle mit allen Ergebnissen:
SQL Server-Version/Build-Nr. | ||||||||||
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 | ||
in Tabelle var deklarieren | ||||||||||
Prozedur erstellen | ||||||||||
Prozess erstellen und ausführen | ||||||||||
lokale Variable deklarieren | N/A | |||||||||
Prozedur erstellen | ||||||||||
Prozess erstellen und ausführen |
Schlussfolgerung
Die Moral von der Geschichte ist also, dass es immer noch keine Lösung für den oben beschriebenen Anwendungsfall gibt, bei dem Sie einen Tabellentyp erstellen, eine Prozedur oder Funktion erstellen möchten, die den Typ verwendet, einen Typ deklariert, das Modul testet und rollt alles zurück. In jedem Fall sind hier die anderen Connect-Elemente, die Sie sich ansehen können. hoffentlich können Sie für sie stimmen und Kommentare hinterlassen, die beschreiben, wie sich dieses Deadlock-Szenario direkt auf Ihr Unternehmen auswirkt:
Ich gehe davon aus, dass in naher Zukunft einige Klarstellungen zu diesen Connect-Elementen hinzugefügt werden, obwohl ich nicht genau weiß, wann sie durchgesetzt werden.