Една от по-рядко срещаните блокирания е тази, при която има един потребител и те се блокират на някакъв системен ресурс. Един скорошен, на който попаднах, е създаване на тип псевдоним, след което деклариране на променлива от този тип в същата транзакция. Представете си, че се опитвате да изпълните единичен тест или тест преди внедряване, да проверите за неуспехи и да върнете обратно във всеки случай, така че да не оставите никаква следа от това, което сте направили. Моделът може да изглежда така:
BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320); GO DECLARE @x TABLE (e EmailAddress); GO ROLLBACK TRANSACTION;
Или, по-вероятно, малко по-сложно:
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;
Първото място, където опитах този код, беше SQL Server 2012 и двата примера се провалиха със следната грешка:
Съобщение 1205, ниво 13, състояние 55, ред 14Транзакцията (идентификатор на процес 57) беше блокирана на ресурсите за заключване с друг процес и беше избрана като жертва на безизходица. Изпълнете отново транзакцията.
И изобщо няма какво да научите от графиката на безизходица:
Отдръпвайки се няколко години назад, си спомням, когато за първи път научих за типове псевдоними, още в SQL Server 2000 (когато те се наричаха User-Defined Data Types). По това време тази безизходица, на която попаднах съвсем наскоро, нямаше да се случи (но това е поне отчасти, защото не можете да декларирате променлива на таблица с тип псевдоним – вижте тук и тук). Изпълних следния код на SQL Server 2000 RTM (8.0.194) и SQL Server 2000 SP4 (8.0.2039) и работи добре:
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;
Разбира се, този сценарий не беше много разпространен по онова време, защото в края на краищата не много хора използваха типове псевдоними на първо място. Въпреки че могат да направят вашите метаданни по-самодокументиращи и подобни на дефиниране на данни, те са царска болка, ако някога искате да ги промените, което може да е тема за друга публикация.
SQL Server 2005 се появи и въведе нов DDL синтаксис за създаване на типове псевдоними:CREATE TYPE
. Това всъщност не реши проблема с промяната на типовете, просто направи синтаксиса малко по-чист. В RTM всички горепосочени примерни кодове работеха добре без блокиране. В SP4 обаче всички те биха блокирали. Следователно, някъде между RTM и SP4, те промениха вътрешната обработка за транзакции, които включват променливи на таблица, използвайки типове псевдоними.
Превъртете няколко години напред към SQL Server 2008, където бяха добавени параметри с стойност на таблица (вижте добър случай на използване тук). Това направи използването на тези типове много по-разпространено и въведе друг случай, в който транзакция, която се опита да създаде и използва такъв тип, щеше да блокира:
BEGIN TRANSACTION; GO CREATE TYPE dbo.Items AS TABLE(Item INT); GO DECLARE @r dbo.Items; GO ROLLBACK TRANSACTION;
Проверих Connect и открих няколко свързани елемента, един от които твърди, че този проблем е отстранен в SQL Server 2008 SP2 и 2008 R2 SP1:
Свързване #365876 :Застой възниква при създаване на дефиниран от потребителя тип данни и обекти, които го използват
Това, което всъщност се отнасяше до следния сценарий, при което простото създаване на съхранена процедура, която се позовава на типа в променлива на таблица, би блокирала в SQL Server 2008 RTM (10.0.1600) и 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;
Това обаче не блокира в SQL Server 2008 SP3 (10.0.5846) или 2008 R2 SP2 (10.50.4295). Така че съм склонен да вярвам на коментарите за елемента Connect – че тази част от грешката е отстранена в 2008 SP2 и 2008 R2 SP1 и никога не е била проблем в по-модерните версии.
Но това все още пропуска възможността за действително поставяне на типа псевдоним чрез какъвто и да е вид истинско тестване. Така че моите единични тестове щяха да успеят, стига всичко, което исках да направя, е да тествам, че мога да създам процедурата – забравете да декларирате типа като локална променлива или като колона в променлива на локална таблица.
Единственият начин да разрешите това е да създадете типа таблица преди стартиране на транзакцията и изрично да я пуснете след това (или по друг начин да я разделите на множество транзакции). Това може да бъде изключително тромаво или дори невъзможно, често автоматизираните рамки за тестване и снопове да променят напълно начина си на работа, за да отчетат това ограничение.
Затова реших да премина през някои тестове в първоначалните и най-новите версии на всички основни версии:SQL Server 2005 RTM, 2005 SP4, 2008 RTM, 2008 SP3, 2008 R2 RTM, 2008 R2 SP2, 2012 RTM, 2012 SP1, и 2014 CTP2 (и да, всичките са инсталирани). Прегледах няколко елемента на Connect и различни коментари, които ме накараха да се чудя кои случаи на употреба се поддържат и къде, и имах странна принуда да разбера кои аспекти на този проблем всъщност са били коригирани. Тествах различни потенциални сценарии за блокиране, включващи типове псевдоними, таблични променливи и параметри с стойност на таблица срещу всички тези компилации; кодът е както следва:
/* 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;
И резултатите отразяват моята история по-горе:SQL Server 2005 RTM не е блокирал нито един от сценариите, но по времето, когато SP4 се появи, всички те са блокирани. Това беше коригирано за сценария "създаване на тип и създаване на процедура", но нито един от другите, през 2008 SP2 и 2008 R2 SP1. Ето таблица, показваща всички резултати:
Версия/версия на 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 | ||
деклариране в таблица var | ||||||||||
създаване на процедура | ||||||||||
създаване и изпълнение на процедура | ||||||||||
деклариране на локална var | N/A | |||||||||
създаване на процедура | ||||||||||
създаване и изпълнение на процедура |
Заключение
И така, моралът на историята е, че все още няма поправка за случая на използване, описан по-горе, където искате да създадете тип таблица, да създадете процедура или функция, която използва типа, да декларирате тип, да тествате модула и да превъртите всичко обратно. Във всеки случай, ето другите елементи на Connect, които можете да разгледате; надявам се, че можете да гласувате за тях и да оставите коментари, описващи как този сценарий в безизходица засяга пряко вашия бизнес:
Напълно очаквам някои пояснения да бъдат добавени към тези елементи на Connect в близко бъдеще, макар че не знам точно кога ще бъдат изпратени.