Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Usando o ROLLBACK de transação no SQL Server

Introdução

Recentemente, um colega meu veio até mim em desespero confessando que ele havia emitido uma declaração de atualização sem uma cláusula WHERE em uma tabela de aplicativos de chave. As implicações no front-end seriam terríveis, então ele veio a mim diretamente porque precisava urgentemente de ajuda para reverter a situação de qualquer maneira antes que os e-mails e a escalada começassem a chegar.

Quando analisamos a situação, descobrimos que as alterações não foram aplicadas no banco de dados secundário. Na maioria dos casos, o atraso entre nossos bancos de dados primários e secundários é de vinte minutos (temos um pouco desconcertante para evitar problemas de desempenho). Como meu colega pediu ajuda imediatamente após perceber o erro, conseguimos recuperar os dados do banco de dados secundário. Descrevi o valor de tal atraso neste artigo .

Revisão do cenário

O cenário que descrevi acima não é incomum. Uma das razões pelas quais isso acontece com usuários regulares do SQL Server é que o SQL Server usa o que é chamado de Transações Implícitas. As transações implícitas são desativadas por padrão, o que significa que o SQL Server não espera que você emita uma instrução COMMIT TRANSACTION no final de cada instrução. Na verdade, cada instrução é confirmada automaticamente. Isso é conveniente e ajuda a evitar situações em que sessões que ainda não foram confirmadas acabam bloqueando recursos e impactando o desempenho. Brent Ozar fornece mais detalhes sobre as implicações de desempenho de IMPLICIT TRANSACTIONS =ON.

No entanto, uma pequena desvantagem dessa configuração (IMPLICIT TRANSACTIONS =OFF) é que os usuários não têm a oportunidade de repensar uma declaração e emitir um ROLLBACK, o que é muito comum no Oracle. A Fig. 1 mostra as opções de consulta ANSI disponíveis no SQL Server Management Studio.


Fig. 1 Padrões ANSI no SQL Server Management Studio

Uso de transações implícitas

Em essência, o problema que enfrentamos nessa configuração padrão ou em nossa ferramenta cliente mais desejável é que não podemos ROLLBACK depois de executar uma instrução SQL. Podemos contornar isso habilitando TRANSAÇÕES IMPLÍCITAS em nossa sessão. Isso nos dará a oportunidade de ROLLBACK transações, se necessário. A Fig. 2 e a Fig. 4 nos mostram que podemos ter essa configuração ativada apenas para uma sessão, mesmo que isso não elimine o risco de a sessão do usuário bloquear outras se um ROLLBACK ou COMMIT não for emitido.


Fig. 2 TRANSAÇÕES IMPLÍCITAS LIGADAS em uma sessão

-- Listing 1: UPDATE Table TAB2 with IMPLICIT_TRANSACTIONS ON
SET IMPLICIT_TRANSACTIONS ON

DECLARE @IMPLICIT_TRANSACTIONS VARCHAR(3) = 'OFF';  
IF ( (2 & @@OPTIONS) = 2 ) SET @IMPLICIT_TRANSACTIONS = 'ON';  
SELECT @IMPLICIT_TRANSACTIONS AS IMPLICIT_TRANSACTIONS;

USE KTrain
GO
SELECT * FROM Tab2;
GO

UPDATE TAB2
SET countryCode='SA'
-- WHERE fname='Joyce';
GO
SELECT * FROM Tab2;
GO



Fig. 3 Todas as linhas atualizadas

Para ilustrar a solução alternativa descrita aqui, vejamos o código SQL na Listagem 1. Vamos supor que um usuário regular do SQL Server, um desenvolvedor júnior, tenha recebido um conjunto de scripts para executar sob determinadas condições . No script, a cláusula WHERE foi comentada porque é esperado que cada vez que eles executem este script, eles alterem o predicado. Claro, este é um caso de uso simples e o risco pode ser tratado de várias maneiras, mas queremos apenas mostrar a possibilidade de realizar um ROLLBACK.

Lembre-se de que já ativamos IMPLICIT TRANSACTION, portanto, quando executarmos essa instrução, o SQL Server estará esperando que façamos COMMIT ou ROLLBACK a transação. A intenção do desenvolvedor é atualizar o countryCode de Joyce Afam para 'SA' desde que ela imigrou para a África do Sul. A Fig. 3 mostra que o desenvolvedor, ao tentar fazer isso, atualizou acidentalmente todas as linhas com o valor SA como countryCode . Eles percebem isso e emitem um ROLLBACK.


Fig. 4 Emissão de ROLLBACK


Fig. 5 TRANSAÇÕES IMPLÍCITAS LIGADAS em outra sessão

No entanto, na outra sessão em que não ativamos TRANSAÇÕES IMPLÍCITAS, descobrimos que o desenvolvedor não conseguiu se recuperar do erro. Eles não podem emitir com sucesso um ROLLBACK neste caso. A recuperação implicaria então a restauração de dados.


Fig. 6 ROLLBACK não é possível sem TRANSAÇÕES IMPLÍCITAS LIGADAS

Usando transações explícitas

Outra abordagem para obter o mesmo efeito é incluir o DML em uma transação declarando explicitamente BEGIN TRAN. Novamente, é muito importante concluir a transação – usando COMMIT ou ROLLBACK. No contexto desta discussão, emitimos um ROLLBACK, pois percebemos que há um erro no código.

-- Listing 2: UPDATE Table TAB2 with Explicit Transaction

BEGIN TRAN 
GO
USE KTrain
GO
SELECT * FROM Tab2;
GO

UPDATE TAB2
SET countryCode='GH'
-- WHERE fname='Joyce';
GO
SELECT * FROM Tab2;
GO

ROLLBACK;

SELECT * FROM Tab2;
GO

- Listing 3: Corrected UPDATE Statement

BEGIN TRAN 
GO
USE KTrain
GO
SELECT * FROM Tab2;
GO

UPDATE TAB2
SET countryCode='SA'
WHERE fname='Joyce';
GO
SELECT * FROM Tab2;
GO

Conclusão

Neste artigo, abordamos brevemente uma boa solução alternativa para criar oportunidades de ROLLBACK e, assim, mitigar os erros do usuário resultantes de DML incorreto. Também destacamos um risco importante dessa abordagem, que é o bloqueio inadvertido. Um DBA pode iniciar investigações sobre a possível presença desse risco consultando sys.dm_tran_session_transactions, sys.dm_tran_locks e objetos de gerenciamento dinâmico semelhantes.

Referências

  1. Corrigindo a perda de dados usando o envio de logs com recuperação atrasada

  2. Definir transações implícitas

  3. Definir transações implícitas como uma má ideia

  4. DMVs para transações