¿Es ROLLBACK una operación rápida?


Respuestas:


14

Para SQL Server, podría argumentar que una operación de confirmación no es más que escribir LOP_COMMIT_XACT en el archivo de registro y liberar bloqueos, que por supuesto será más rápido que el ROLLBACK de cada acción que realizó su transacción desde BEGIN TRAN.

Si está considerando cada acción de una transacción, no solo la confirmación, todavía argumentaría que su declaración no es cierta. Excluyendo factores externos, la velocidad del disco de registro en comparación con la velocidad del disco de datos, por ejemplo, es probable que la reversión de cualquier trabajo realizado por una transacción sea más rápida que hacer el trabajo en primer lugar.

Una reversión es leer un archivo secuencial de cambios y aplicarlos a páginas de datos en memoria. El "trabajo" original tenía que generar un plan de ejecución, adquirir páginas, unir filas, etc.

Editar: El depende poco ...

@JackDouglas señaló este artículo que describe una de las situaciones en las que la reversión puede llevar mucho más tiempo que la operación original. El ejemplo es una transacción de 14 horas, inevitablemente usando paralelismo, que demora más de 48 horas en revertirse porque la reversión es principalmente de un solo subproceso. Lo más probable es que también esté agitando el grupo de búferes repetidamente, por lo que ya no está invirtiendo los cambios en las páginas en memoria.

Entonces, una versión revisada de mi respuesta anterior. ¿Cuánto más lento es el retroceso? Todas las demás cosas consideradas, para una transacción OLTP típica, no lo es. Fuera de los límites de lo típico, puede llevar más tiempo "deshacer" que "hacer" pero (¿es esto un posible trabalenguas?) ¿Por qué dependerá de cómo se hizo el "hacer".

Edit2: a continuación de la discusión en los comentarios, aquí hay un ejemplo muy artificial para demostrar que el trabajo realizado es el factor principal para determinar el gasto relativo de commit vs rollback como operaciones.

Cree dos tablas y empaquéelas de manera ineficiente (espacio desperdiciado 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

Ejecute una consulta de actualización "incorrecta", midiendo el tiempo necesario para realizar el trabajo y el tiempo necesario para emitir la confirmación.

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

Haga lo mismo nuevamente pero emita y mida la reversión.

    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

Con @ Rows = 1 obtengo un razonablemente consistente:

  • 5500ms para la búsqueda / actualización
  • 3ms commit
  • Rollback de 1 ms

Con @ filas = 100:

  • 8500ms buscar / actualizar
  • 15 ms de compromiso
  • 15 ms de reversión

Con @ Filas = 1000:

  • 15000 ms buscar / actualizar
  • 10 ms de compromiso
  • Reversión de 500 ms

De vuelta a la pregunta original. Si está midiendo el tiempo necesario para hacer el trabajo más el compromiso, la reversión está ganando sin duda porque la mayor parte de ese trabajo se dedica a encontrar la fila para actualizar, en lugar de modificar los datos. Si observa la operación de confirmación de forma aislada, debe quedar claro que la confirmación hace muy poco "trabajo" como tal. Comprometerse es "Ya terminé".


2
'menos trabajo' no es necesariamente 'más rápido'
Jack Douglas

Sabía que eso begin transolo aumenta el contador de transacciones. Si te entendí, ¿rdbms está haciendo todas las tareas (une filas, genera planes de ejecución ...) en COMMIT?
garik

3
No, todo el trabajo se realiza antes de comprometerse. La operación de confirmación en sí misma hace relativamente poco.
Mark Storey-Smith

@ Mark He realizado algunas pruebas aproximadas insertando filas de 2m y confirmando o retrocediendo. El tiempo total, incluida la reversión, varió de 10 a 30 segundos, en comparación con entre 6 y 14 segundos durante el tiempo total, incluido el compromiso. YMMV, por supuesto, pero esto indica que la reversión del estadio es casi tan larga o más larga que la transacción original, al menos en mi entorno.
Jack Douglas

2
Si midiera el tiempo para que se complete la operación de confirmación, esperaría que fuera mínimo a menos que se emita un punto de control al mismo tiempo (que es independiente y no está relacionado). Ese es mi punto, el commit hace muy poco, mientras que el rollback hace todo lo que sucedió antes del commit y un poco más. La variación en sus pruebas sugiere otros factores en juego, pero ciertamente intentaré reunir algunos guiones más adelante.
Mark Storey-Smith

13

Para Oracle, la reversión puede llevar muchas veces más tiempo que el tiempo que llevó realizar los cambios que están retrocediendo. Esto a menudo no importa porque

  1. No se retienen bloqueos mientras la transacción se revierte
  2. Se maneja mediante un proceso en segundo plano de baja prioridad

Para SQL Server, no estoy seguro de si la situación es la misma, pero alguien más dirá si no es así ...

En cuanto a "por qué", diría que rollbackdebería ser raro , generalmente solo si algo salió mal, y por supuesto commites probable que sea mucho más común, por lo que tiene sentido optimizarlo paracommit


9

La reversión no es solo "oh, no importa", en muchos casos realmente tiene que deshacer lo que ya había hecho. No existe una regla que establezca que la operación de reversión siempre será más lenta o más rápida que la operación original, aunque incluso si la transacción original se ejecutó en paralelo, la reversión es de un solo subproceso. Si está esperando, sugiero que es más seguro seguir esperando.

Todo esto cambia con SQL Server 2019, por supuesto, y la Recuperación acelerada de la base de datos (que, con una penalización que también es variable, permite la reversión instantánea independientemente del tamaño de los datos).


2
Y todos hemos tenido esa conversación de "demorar años, reiniciemos" en algún momento, ¿verdad?
Mark Storey-Smith

He visto a muchos clientes hacerlo. Algunos salen relativamente indemnes, otros son mucho menos afortunados.
Aaron Bertrand

1
@ MarkStorey-Smith: si reinicia a mitad de la reversión, ¿no debe SQL Server continuar su reversión al inicio de todos modos?
Nick Chammas

2
@Nick, eso depende: si la reversión se bloqueó antes del reinicio, por ejemplo, podría comportarse mucho más rápido después de reiniciar el servicio porque ese otro proceso acaba de finalizar. Hay MUCHO "qué pasaría si" en este escenario: cada vez que reinicia un servidor o reinicia un servicio para "solucionar" un problema, probablemente haya algunos problemas mucho más serios en juego.
Aaron Bertrand

2
@ Nick, sí, eso es exactamente lo que sucede. Mi comentario tenía la intención de ser "lengua en la mejilla", en tanto que inevitablemente terminas teniendo que explicar eso para desencadenar a las personas felices que quieren reiniciar cada vez que algo no se comporta como se esperaba.
Mark Storey-Smith

8

No todas las transacciones tendrán una actividad de compromiso mucho mejor que su reversión. Uno de estos casos es la operación de eliminación en SQL. Cuando una transacción elimina filas, estas filas se marcan como registros fantasma. Una vez que se emite una confirmación y se inicia una tarea de limpieza de registros fantasma, solo se eliminan estos registros.

Si se emitió una reversión, solo elimina las marcas fantasma de estos registros, y no las instrucciones de inserción intensiva.


Buen ejemplo de cómo ciertas operaciones están optimizadas para la reversión.
Mark Storey-Smith

5

No todos lo son. PostgreSQL no necesita más tiempo para retroceder que para comprometerse, ya que las dos operaciones son efectivamente idénticas en términos de E / S de disco. En realidad, no creo que se trate de optimizar tanto para el compromiso como de las otras consultas para las que se está optimizando.

La pregunta básica es cómo aborda el diseño en el disco y cómo esto afecta la confirmación frente a la reversión. Las principales bases de datos que retroceden más lentamente que el compromiso tienden a mover datos, particularmente de tablas agrupadas, fuera de las estructuras de datos principales y colocarlos en un segmento de reversión cuando se actualizan datos. Esto significa que para comprometerse simplemente descarte el segmento de reversión, pero para retroceder debe copiar todos los datos.

Para PostgreSQL, todas las tablas son tablas de montón y los índices están separados. Esto significa que al retroceder o confirmar, no es necesario reorganizar los datos. Esto hace que commit y rollback sean rápidos.

Sin embargo, hace que otras cosas sean un poco más lentas. Una búsqueda de clave principal, por ejemplo, tiene que atravesar un archivo de índice y luego debe golpear la tabla de montón (suponiendo que no sean aplicables los índices de cobertura). Esto no es un gran problema, pero agrega una búsqueda de página adicional o tal vez incluso algunas búsquedas de página aleatorias (si se han producido muchas actualizaciones en esa fila) para buscar otra información y visibilidad.

Sin embargo, la velocidad aquí no es una cuestión de optimización en PostgreSQL para operaciones de escritura frente a operaciones de lectura. Es una falta de voluntad para privilegiar algunas operaciones de lectura sobre otras. En consecuencia, PostgreSQL funciona en promedio aproximadamente tan bien como los otros db's. Es solo ciertas operaciones que pueden ser más rápidas o más lentas.

Entonces, creo que la respuesta real es que los db están optimizados para ciertas cargas de trabajo en el lado de lectura y esto lleva a desafíos en el lado de escritura. Por lo general, cuando hay una pregunta, los commits suelen ser, aunque no siempre, favorecidos sobre los retrocesos. Sin embargo, esto depende de las implicaciones de hacer cualquiera de las dos (las actualizaciones son diferentes a las eliminaciones).


Buena respuesta, pero una pequeña objeción: "Para PostgreSQL, todas las tablas son tablas de montón y los índices están separados. Esto significa que al revertir o confirmar, no hay que reorganizar los datos", esta no es la razón por la que no hay datos que se reorganizará, más bien se debe a que "los principales db que retroceden más lentamente que los que se comprometen tienden a mover datos", y pg no lo hace, como usted mencionó. Oracle también usa de forma predeterminada el almacenamiento dinámico: la principal diferencia es que Oracle usa 'deshacer' y reclama todo el espacio en commit / rollback en lugar de ir a la ruta 'vacía'.
Jack Douglas
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.