Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

A seguito di un deadlock di transazione singola tra le versioni di SQL Server

Uno dei deadlock meno comuni è quello in cui è presente un singolo utente e si blocca su alcune risorse di sistema. Uno di recente che ho riscontrato è la creazione di un tipo di alias, quindi la dichiarazione di una variabile di quel tipo, all'interno della stessa transazione. Immagina di provare a eseguire uno unit test o un test di pre-distribuzione, verificare la presenza di errori e comunque eseguire il rollback in modo da non lasciare traccia di ciò che avevi fatto. Lo schema potrebbe assomigliare a questo:

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

O, più probabilmente, un po' più complesso:

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;

Il primo posto in cui ho provato questo codice è stato SQL Server 2012 ed entrambi gli esempi hanno avuto esito negativo con il seguente errore:

Msg 1205, livello 13, stato 55, riga 14
La transazione (ID processo 57) è stata bloccata su risorse di blocco con un altro processo ed è stata scelta come vittima del deadlock. Eseguire nuovamente la transazione.

E non c'è molto da imparare dal grafico dello stallo:

Tornando indietro di alcuni anni, ricordo quando ho appreso per la prima volta i tipi di alias, in SQL Server 2000 (quando erano chiamati tipi di dati definiti dall'utente). A quel tempo, questo deadlock in cui mi sono imbattuto più di recente non si sarebbe verificato (ma questo è almeno in parte dovuto al fatto che non potevi dichiarare una variabile di tabella con un tipo di alias – vedi qui e qui). Ho eseguito il codice seguente su SQL Server 2000 RTM (8.0.194) e SQL Server 2000 SP4 (8.0.2039) e ha funzionato perfettamente:

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;

Naturalmente, questo scenario non era molto diffuso all'epoca perché, dopotutto, non molte persone usavano i tipi di alias in primo luogo. Sebbene possano rendere i tuoi metadati più autodocumentanti e simili alla definizione dei dati, sono una vera seccatura se vuoi cambiarli, il che potrebbe essere un argomento per un altro post.

SQL Server 2005 è arrivato e ha introdotto la nuova sintassi DDL per la creazione di tipi di alias:CREATE TYPE . Questo non ha davvero risolto il problema con la modifica dei tipi, ha solo reso la sintassi un po' più pulita. In RTM, tutti gli esempi di codice precedenti hanno funzionato perfettamente senza deadlock. In SP4, tuttavia, si sarebbero tutti bloccati. Pertanto, da qualche parte tra RTM e SP4, hanno modificato la gestione interna per le transazioni che coinvolgevano variabili di tabella utilizzando tipi di alias.

Avanti veloce di alcuni anni a SQL Server 2008, in cui sono stati aggiunti parametri con valori di tabella (vedi un buon caso d'uso qui). Ciò ha reso l'uso di questi tipi molto più diffuso e ha introdotto un altro caso in cui una transazione che ha tentato di creare e utilizzare un tipo di questo tipo si sarebbe bloccata:

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

Ho controllato Connect e ho trovato diversi elementi correlati, uno dei quali affermava che questo problema è stato risolto in SQL Server 2008 SP2 e 2008 R2 SP1:

Connect #365876 :si verifica un deadlock durante la creazione del tipo di dati definito dall'utente e degli oggetti che lo utilizzano

Ciò a cui si riferiva effettivamente era lo scenario seguente, in cui la semplice creazione di una stored procedure che faceva riferimento al tipo in una variabile di tabella avrebbe provocato un deadlock in SQL Server 2008 RTM (10.0.1600) e 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;

Tuttavia, questo non si blocca in SQL Server 2008 SP3 (10.0.5846) o 2008 R2 SP2 (10.50.4295). Quindi tendo a credere ai commenti sull'elemento Connect:che questa parte del bug è stata corretta nel 2008 SP2 e 2008 R2 SP1 e non è mai stato un problema nelle versioni più moderne.

Ma questo esclude comunque la possibilità di sottoporre effettivamente il tipo di alias a qualsiasi tipo di vero test. Quindi i miei unit test avrebbero avuto successo purché tutto ciò che volevo fare fosse verificare che potevo creare la procedura, dimenticando di dichiarare il tipo come variabile locale o come colonna in una variabile di tabella locale.

L'unico modo per risolvere questo problema è creare il tipo di tabella prima di iniziare la transazione e rilasciarlo esplicitamente in seguito (o dividerlo in più transazioni). Potrebbe essere estremamente ingombrante, o addirittura impossibile, avere spesso framework e imbracature di test automatizzati che cambiano completamente il modo in cui operano per tenere conto di questa limitazione.

Quindi ho deciso di eseguire alcuni test nelle build iniziali e più recenti di tutte le versioni principali:SQL Server 2005 RTM, 2005 SP4, 2008 RTM, 2008 SP3, 2008 R2 RTM, 2008 R2 SP2, 2012 RTM, 2012 SP1, e 2014 CTP2 (e sì, li ho tutti installati). Avevo esaminato diversi elementi Connect e vari commenti che mi hanno lasciato chiedendomi quali casi d'uso fossero supportati e dove, e ho avuto una strana pulsione a scoprire quali aspetti di questo problema erano stati effettivamente risolti. Ho testato vari potenziali scenari di deadlock che coinvolgono tipi di alias, variabili di tabella e parametri con valori di tabella rispetto a tutte queste build; il codice è il seguente:

/* 
  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;

E i risultati riflettono la mia storia sopra:SQL Server 2005 RTM non ha avuto un deadlock in nessuno degli scenari, ma quando SP4 è stato implementato, tutti si sono bloccati. Questo è stato corretto per lo scenario "crea un tipo e crea una procedura", ma nessuno degli altri, nel 2008 SP2 e 2008 R2 SP1. Ecco una tabella che mostra tutti i risultati:

Versione SQL Server/Build #
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
Tipo alias dichiara nella tabella var
crea procedura
crea ed esegui proc
Tipo di tabella dichiara var locale N/D
crea procedura
crea ed esegui proc

Conclusione

Quindi, la morale della storia è che non c'è ancora alcuna soluzione per il caso d'uso descritto sopra, in cui si desidera creare un tipo di tabella, creare una procedura o una funzione che utilizzi il tipo, dichiarare un tipo, testare il modulo e tirare tutto indietro. In ogni caso, ecco gli altri elementi Connect da guardare; si spera che tu possa votarli e lasciare commenti che descrivono in che modo questo scenario di stallo influisce direttamente sulla tua attività:

  • Connect #581193 :la creazione di un tipo di tabella e il suo utilizzo nella stessa transazione provoca un deadlock
  • Connetti #800919 :Problema nella creazione di una funzione con valore restituito Digitare una transazione con il tipo definito dall'utente nella tabella che viene creata nello stesso ambito della transazione
  • Connect #804365 :si verifica un deadlock quando un tipo di tabella definito dall'utente viene creato e utilizzato in una transazione

    Mi aspetto che alcuni chiarimenti vengano aggiunti a questi elementi Connect nel prossimo futuro, anche se non so esattamente quando verranno inoltrati.