O ROLLBACK é uma operação rápida?

Respostas:

14

Para o SQL Server, você poderia argumentar que uma operação de confirmação nada mais é do que gravar LOP_COMMIT_XACT no arquivo de log e liberar bloqueios, o que obviamente será mais rápido que o ROLLBACK de todas as ações que sua transação executou desde BEGIN TRAN.

Se você está considerando todas as ações de uma transação, não apenas o commit, eu ainda argumentaria que sua afirmação não é verdadeira. Excluindo fatores externos, a velocidade do disco de log em comparação com a velocidade do disco de dados, por exemplo, é provável que a reversão de qualquer trabalho realizado por uma transação seja mais rápida do que fazer o trabalho em primeiro lugar.

Uma reversão está lendo um arquivo seqüencial de alterações e aplicando-as às páginas de dados na memória. O "trabalho" original tinha que gerar um plano de execução, adquirir páginas, juntar linhas etc.

Edit: O depende pouco ...

O @JackDouglas apontou para este artigo, que descreve uma das situações em que a reversão pode demorar significativamente mais que a operação original. O exemplo é uma transação de 14 horas, inevitavelmente usando paralelismo, que leva mais de 48 horas para retroceder porque o retrocesso é principalmente de thread único. Você provavelmente também estaria agitando o buffer pool repetidamente, para não mais reverter as alterações nas páginas na memória.

Portanto, uma versão revisada da minha resposta anterior. Quanto mais lenta é a reversão? Todas as outras coisas consideradas, para uma transação OLTP típica, não é. Fora dos limites do típico, pode levar mais tempo para "desfazer" do que "fazer", mas (isso é um trava-língua em potencial?) Por que dependerá de como o "fazer" foi feito.

Edit2: Seguindo a discussão nos comentários, aqui está um exemplo muito elaborado para demonstrar que o trabalho que está sendo feito é o principal fator na determinação da despesa relativa de consolidação versus reversão como operações.

Crie duas tabelas e embale-as ineficientemente (espaço desperdiçado por página):

SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;
SET NOCOUNT ON;
GO

CREATE TABLE dbo.Foo
(
    col1 INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
    , col2 CHAR(4000) NOT NULL DEFAULT REPLICATE('A', 4000)
)

CREATE TABLE dbo.Bar
(
    col1 INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
    , col2 CHAR(4000) NOT NULL DEFAULT REPLICATE('A', 4000)
)
GO

INSERT dbo.Foo DEFAULT VALUES
GO 100000

INSERT dbo.Bar DEFAULT VALUES
GO 100000

Execute uma consulta de atualização "ruim", medindo o tempo necessário para realizar o trabalho e o tempo necessário para emitir a confirmação.

DECLARE 
    @StartTime DATETIME2
    , @Rows INT

SET @Rows = 1

CHECKPOINT
DBCC DROPCLEANBUFFERS

BEGIN TRANSACTION

SET @StartTime = SYSDATETIME()

UPDATE
    dbo.bar
SET
    col2 = REPLICATE('B', 4000)
FROM
    dbo.bar b
INNER JOIN
    (
    SELECT TOP(@Rows)
        col1
    FROM
        dbo.foo
    ORDER BY
        NEWID()
    ) f
ON  f.col1 = b.col1
OPTION (MAXDOP 1)

SELECT 'Find and update row', DATEDIFF(ms, @StartTime, SYSDATETIME())

SET @StartTime = SYSDATETIME()

COMMIT TRANSACTION

SELECT 'Commit', DATEDIFF(ms, @StartTime, SYSDATETIME())
GO

Faça o mesmo novamente, mas emita e meça a reversão.

    DECLARE 
    @StartTime DATETIME2
    , @Rows INT

SET @Rows = 1

CHECKPOINT
DBCC DROPCLEANBUFFERS

BEGIN TRANSACTION

SET @StartTime = SYSDATETIME()

UPDATE
    dbo.bar
SET
    col2 = REPLICATE('B', 4000)
FROM
    dbo.bar b
INNER JOIN
    (
    SELECT TOP(@Rows)
        col1
    FROM
        dbo.foo
    ORDER BY
        NEWID()
    ) f
ON  f.col1 = b.col1
OPTION (MAXDOP 1)

SELECT 'Find and update row', DATEDIFF(ms, @StartTime, SYSDATETIME())

SET @StartTime = SYSDATETIME()

ROLLBACK TRANSACTION

SELECT 'Rollback', DATEDIFF(ms, @StartTime, SYSDATETIME())
GO

Com @ Rows = 1, fico razoavelmente consistente:

  • 5500ms para a localização / atualização
  • 3ms confirmar
  • Reversão de 1 ms

Com @ linhas = 100:

  • 8500ms encontrar / atualizar
  • 15ms confirmar
  • Reversão de 15ms

Com @ linhas = 1000:

  • 15000ms encontrar / atualizar
  • 10ms confirmar
  • Reversão de 500ms

Voltar à pergunta original. Se você está medindo o tempo gasto para realizar o trabalho mais a confirmação, a reversão está diminuindo porque a maior parte desse trabalho é gasta na busca da linha a ser atualizada, na verdade não na modificação de dados. Se você estiver analisando a operação de consolidação isoladamente, deve ficar claro que a consolidação faz muito pouco "trabalho" como tal. Confirmar é "Estou pronto".

Mark Storey-Smith
fonte
2
'menos trabalho' é não necessariamente 'mais rápido'
Jack Douglas
Eu sabia que isso begin tranapenas aumenta o contador de transações. Se eu entendi, o rdbms está executando todas as tarefas (une linhas, gera planos de execução ...) no COMMIT?
garik
3
Não, todo o trabalho é feito antes da confirmação. A operação de confirmação em si faz comparativamente pouco.
Mark-Storey-Smith
@ Mark Eu fiz alguns testes aproximados e rápidos, inserindo linhas de 2m e comprometendo ou revertendo. O tempo total incluindo reversão variou de 10s a 30s, contra 6s e 14s no tempo total, incluindo confirmação. YMMV, é claro, mas isso indica que a reversão da estimativa é quase tão longa quanto a transação original, pelo menos no meu ambiente.
31411 Jack Douglas
2
Se você medisse o tempo para a operação de confirmação ser concluída, seria de esperar que fosse mínimo, a menos que um ponto de verificação fosse emitido ao mesmo tempo (que é separado e não relacionado). Esse é o meu ponto de vista, o commit faz muito pouco, enquanto o rollback faz tudo o que aconteceu antes do commit e um pouco mais. A variação em seus testes sugere outros fatores em jogo, mas certamente tentarei montar alguns scripts mais tarde.
Mark-Storey-Smith
13

Para o Oracle, a reversão pode demorar muito mais do que o tempo necessário para fazer as alterações que estão sendo revertidas. Isso geralmente não importa porque

  1. Nenhum bloqueio é retido enquanto a transação está sendo revertida
  2. É tratado por um processo em segundo plano de baixa prioridade

Para o SQL Server, não tenho certeza se a situação é a mesma, mas alguém dirá se não é ...

Quanto ao "porquê", eu diria que isso rollbackdeve ser raro , geralmente apenas se algo der errado, e commité claro que provavelmente será muito mais comum - por isso, faz sentido otimizar paracommit

Jack Douglas
fonte
9

A reversão não é apenas "oh, não importa" - em muitos casos, ela realmente precisa desfazer o que já havia feito. Não há regra de que a operação de reversão seja sempre mais lenta ou sempre mais rápida que a operação original, embora, mesmo que a transação original tenha sido executada em paralelo, a reversão seja de thread único. Se você está esperando, sugiro que é mais seguro continuar esperando.

Isso tudo muda com o SQL Server 2019, é claro, e a Accelerated Database Recovery (que, com uma penalidade também variável) permite reversão instantânea, independentemente do tamanho dos dados.

Aaron Bertrand
fonte
2
E todos nós tivemos a conversa "está demorando muito para reverter, vamos reiniciá-la" em algum momento, certo?
Mark-Storey-Smith
Eu já vi muitos clientes fazerem isso. Alguns saem relativamente incólumes, outros têm muito menos sorte.
Aaron Bertrand
1
@ MarkStorey-Smith - Se você reiniciar o meio da reversão, o SQL Server não precisará continuar sua reversão na inicialização?
Nick Chammas
2
@ Nick que depende - se a reversão foi bloqueada antes da reinicialização, por exemplo, ela pode se comportar muito mais rapidamente após a reinicialização do serviço, porque esse outro processo foi interrompido. Há muito "e se" nesse cenário - sempre que você reinicia um servidor ou reinicia um serviço para "corrigir" um problema, provavelmente existem problemas muito mais sérios em jogo.
Aaron Bertrand
2
@ Nick, sim, é exatamente o que acontece. Meu comentário pretendia ser "simplório", de tal forma que você inevitavelmente acaba explicando isso ao gatilho de pessoas felizes que desejam reiniciar sempre que algo não está se comportando como esperado.
Mark-Storey-Smith #
8

Nem todas as transações terão sua atividade de confirmação com desempenho muito melhor que sua reversão. Um desses casos é a operação de exclusão no SQL. Quando uma transação exclui linhas, essas linhas são marcadas como registros fantasmas. Depois que uma confirmação é emitida e uma tarefa de limpeza de registro fantasma é iniciada, somente esses registros são 'excluídos'.

Se uma reversão foi emitida, ela remove as marcações fantasmas desses registros, e não as instruções de inserção intensivas.

StanleyJohns
fonte
Bom exemplo de como certas operações são otimizadas para reversão.
Mark-Storey-Smith
5

Nem todos são. O PostgreSQL não demora mais para reverter do que para confirmar, pois as duas operações são efetivamente idênticas em termos de E / S do disco. Na verdade, não acho que seja uma questão de ser otimizado para o commit, mas sim para quais outras consultas estamos otimizando.

A questão básica é como você lida com o layout do disco e como isso afeta a confirmação versus a reversão. Os principais bancos de dados que retrocedem mais lentamente do que confirmam tendem a mover dados, principalmente de tabelas em cluster, para fora das principais estruturas de dados e colocá-los em um segmento de reversão ao atualizar dados. Isso significa que, para confirmar, basta descartar o segmento de reversão, mas, para reverter, é necessário copiar todos os dados.

Para o PostgreSQL, todas as tabelas são tabelas de heap e os índices são separados. Isso significa que, ao reverter ou confirmar, nenhum dado precisa ser reorganizado. Isso faz com que a consolidação e a reversão sejam rápidas.

No entanto, isso torna algumas outras coisas um pouco mais lentas. Uma pesquisa de chave primária, por exemplo, precisa percorrer um arquivo de índice e, em seguida, acessar a tabela de heap (supondo que não haja índices de cobertura aplicáveis). Isso não é um grande negócio, mas adiciona uma pesquisa de página extra ou talvez algumas pesquisas de página aleatórias (se muitas atualizações ocorreram nessa linha) para verificar outras informações e visibilidade.

A velocidade aqui, no entanto, não é uma questão de otimização no PostgreSQL para operações de gravação versus operações de leitura. É uma relutância em privilegiar algumas operações de leitura acima de outras. Consequentemente, o PostgreSQL executa, em média, tanto quanto os outros bancos de dados. Apenas algumas operações podem ser mais rápidas ou mais lentas.

Portanto, acho que a resposta real é que os bancos de dados são otimizados para determinadas cargas de trabalho no lado de leitura e isso leva a desafios no lado de gravação. Geralmente, onde há uma pergunta, os commits geralmente são, embora nem sempre, favorecidos em relação às reversões. No entanto, isso depende das implicações de se executar uma delas (as atualizações são diferentes das exclusões).

Chris Travers
fonte
Boa resposta, mas uma leve queixa: "Para o PostgreSQL, todas as tabelas são tabelas de heap e os índices são separados. Isso significa que, ao reverter ou confirmar, nenhum dado precisa ser reorganizado", esse não é o motivo pelo qual nenhum dado deve ser recuperado. ser reorganizados, é porque "Os principais bancos de dados que retrocedem mais lentamente do que comprometendo tendem a mover dados", e a página não, como você mencionou. A Oracle também padroniza o armazenamento em heap: a principal diferença é que a Oracle usa 'desfazer' e recupera todo o espaço na confirmação / reversão, em vez de seguir a rota do 'vácuo'.
Jack Douglas