Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

След блокиране на една транзакция между версиите на SQL Server

Една от по-рядко срещаните блокирания е тази, при която има един потребител и те се блокират на някакъв системен ресурс. Един скорошен, на който попаднах, е създаване на тип псевдоним, след което деклариране на променлива от този тип в същата транзакция. Представете си, че се опитвате да изпълните единичен тест или тест преди внедряване, да проверите за неуспехи и да върнете обратно във всеки случай, така че да не оставите никаква следа от това, което сте направили. Моделът може да изглежда така:

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, които можете да разгледате; надявам се, че можете да гласувате за тях и да оставите коментари, описващи как този сценарий в безизходица засяга пряко вашия бизнес:

  • Свързване #581193 :Създаването на тип таблица и използването му в същата транзакция води до блокиране
  • Свързване #800919 :Проблем при Създаване на функция с Тип на връщане на TableValue в транзакция с дефиниран от потребителя тип в таблица, която е създадена в същия обхват на транзакцията
  • Свързване #804365 :Застой възниква, когато се създаде дефиниран от потребителя тип таблица и се използва в една транзакция

    Напълно очаквам някои пояснения да бъдат добавени към тези елементи на Connect в близко бъдеще, макар че не знам точно кога ще бъдат изпратени.


    1. Database
    2.   
    3. Mysql
    4.   
    5. Oracle
    6.   
    7. Sqlserver
    8.   
    9. PostgreSQL
    10.   
    11. Access
    12.   
    13. SQLite
    14.   
    15. MariaDB
    1. SQL Server Промяна на местоположението на TempDB файла

    2. 3 начина да получите списък с бази данни в SQL Server (T-SQL)

    3. Защо прехвърлянето от float към varchar се закръгля в SQL Server?

    4. Извличане на дефиниция на колона за набор от резултати от запомнени процедури

    5. Как да шифровате съхранена процедура в SQL Server