Exemplo de Deadlock no SQL Server

Deadlock acontece quando dois ou mais processos são impedidos de prosseguir pois um estar bloqueando o outro, como exemplo, duas pessoas estão querendo usar o mesmo telefone para ligar para números diferentes, enquanto uma delas não ceder sua vez, nenhuma irá conseguir ligar.

No nível de transações de banco de dados não é muito diferente, teremos duas tabelas em nosso banco de dados:

CREATE TABLE Pagamentos (ID INT PRIMARY KEY)
GO
CREATE TABLE Contas (ID INT PRIMARY KEY)
GO

Agora, imagine que um cliente, o senhor 52, irá abrir uma transação para inserir um registro na tabela de pagamentos:

BEGIN TRAN
GO
INSERT INTO Pagamentos VALUES(1)
GO

E ao mesmo tempo, outro cliente, o senhor 55, irá inserir um registro na tabela de contas:

BEGIN TRAN
GO
INSERT INTO Contas VALUES(1)
GO

Até ai, tudo bem, as transações estão abertas, o senhor 55 e o senhor 52 estão utilizando tabelas totalmente diferentes. Mas o senhor 55 lembra que além de inserir um registro na tabela de contas, ele precisa inserir um registro na tabela de pagamentos na mesma transação, assim ele terá que esperar que o senhor 52 termine a transação dele primeiro.

INSERT INTO Pagamentos VALUES(1)
GO

Executando...

Por enquanto nada crítico, é só um cliente esperando o outro terminar.

Mas o senhor 52 também inventa de querer inserir um registro na tabela de Contas:

INSERT INTO Contas VALUES(1)
GO

Dai acontece o Deadlock, o senhor 52 esperando o senhor 55 terminar a transação dele, e o senhor 55 esperando o senhor 52. Como se trata de uma Deadlock bem simples, o SQL Server vai rapidamente escolher qual dos dois clientes tem a preferência, e por consequência definirá sua vítima, que é o cliente que terá que morrer para que aquele que possui preferência consiga terminar seu processo.

E assim, no meu cenário, senhor 52 foi brutalmente assassinado (a transação sofre um ROLLBACK pelo próprio SQL Server, de forma que todos os rastros da vítima sejam apagados):

RIP

Msg 1205, Level 13, State 47, Line 4
Transaction (Process ID 52) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

E em homenagem a todos os processos que diariamente são vítimas de Deadlock:

Em homenagem a todos os processos que diariamente são vítimas de Deadlock

|| EDIT 2012-03-29
|| Via: http://pessoalex.wordpress.com/2012/03/29/exemplo-de-deadlock-no-sql-server/

Deadlock

Trabalhando com uma transação em várias conexões

Pode se tratar de uma feature deprecated (não existirá em edições futuras SQL Server), mas é bem útil quando você necessita em várias conexões (ou seções) compartilhar objetos/registros que estão com lock exclusivo por uma transação, ou iniciar uma transação em uma aplicação e concluir em outra aplicação.

A primeira procedure para esta solução é a sp_getbindtoken, que permite recuperar o token da sessão.

A segunda procedure é a sp_bindsession, permite em outra conexão utilizar o token recuperado da transação, para juntar as duas seções.

Para um exemplo simples, temos a seção 53, iniciando uma transação. Nesta transação, criamos uma tabela com dois registros, por fim, recuperamos o Token da sessão:

DECLARE @bind_token varchar(255)

BEGIN TRAN

CREATE TABLE T (
	ID INT
)

INSERT INTO T
VALUES (1),(2),(3)

EXEC sp_getbindtoken @bind_token OUTPUT

SELECT @bind_token

Numa nova seção (56), juntamos esta à seção 53 por meio do Token, para acessarmos a tabela criada na transação. E por fim damos ROLLBACK na transação iniciada na seção 53, por meio desta nova seção.

EXEC sp_bindsession 'c86>CI;@aAK;SNG1-3^@Y]5---/gG---'
GO

SELECT * FROM T

ROLLBACK

Mas é com pesar que perderemos esta feature nas futuras edições do SQL Server.

Agradecimentos ao Darlan O. da Luz pelo desafio.