¿Cómo son diferentes estas reversiones de SQL Server?


13

En SQL Server 2008 R2, ¿en qué se diferencian estos dos retrocesos?

  1. Ejecute una ALTERdeclaración, durante unos minutos, y luego presione 'Cancelar ejecución'. Tarda unos minutos en retroceder por completo.

  2. Ejecute la misma ALTERinstrucción, pero asegúrese de que el LDFarchivo no sea lo suficientemente grande para que se complete correctamente. Una vez que LDFse alcanza el límite y no se permite el 'crecimiento automático', la ejecución de la consulta se detiene inmediatamente (o se produce una reversión) con este mensaje de error:

The statement has been terminated.
Msg 9002, Level 17, State 4, Line 1
The transaction log for database 'SampleDB' is full. 
To find out why space in the log cannot be reused, see the 
log_reuse_wait_desc column in sys.databases

¿Cómo son estos dos diferentes en los siguientes puntos?

  1. ¿Por qué es instantáneo el segundo 'retroceso'? No estoy completamente seguro de si podría llamarse una reversión. Supongo que el registro de transacciones se escribe a medida que avanza la ejecución y una vez que se da cuenta de que no hay suficiente espacio para completar la tarea por completo, simplemente se detiene con algún mensaje de "finalización", sin confirmación.

  2. ¿Qué sucede cuando la primera reversión toma tanto tiempo (es una reversión de un solo subproceso)?
    2.1. ¿SQL Server retrocede y deshace las entradas realizadas en el LDFarchivo?
    2.2. El LDFtamaño del archivo se reduce al final de la reversión (desde DBCC SQLPERF(LOGSPACE))

  3. Una pregunta adicional: durante el segundo escenario, SQL Server comienza a consumir LDFarchivos con bastante rapidez. En mi caso, aumentó del 18% al 90% en los primeros minutos (<4 minutos). Pero una vez que alcanzó el 99%, permaneció allí durante otros 8 minutos, mientras que el uso fluctuaba entre 99.1% y 99.8%. Sube (99.8%) y baja (99.2%) y sube nuevamente (99.7%) y baja (99.5%) algunas veces antes de que se produzca el error. ¿Qué está pasando detrás de escena?

Se agradece cualquier enlace de MSDN que pueda ayudar a explicar esto más.

A sugerencia de Ali Razeghi, estoy agregando perfmon: Disk Bytes/sec

Escenario 1:

escenario 1

Escenario 2:

Escenario 2


Solo un comentario rápido: ¡tamaño de archivo! = Espacio utilizado dentro de un archivo.
Aaron Bertrand

@ Aaron Sí, estoy familiarizado con eso. Usé DBCC SQLPERF (LOGSPACE) para medir el uso. El archivo LDF permaneció igual durante toda la duración, ya que limité el tamaño del archivo a 10 GB. El uso interno varía.
ToC

1
Sospecho que la segunda reversión parece instantánea, porque el error se informa después de que se completa la reversión. Estos son probablemente los 8 minutos que observa en 3., donde el uso de LDF permanece bastante constante.
mustaccio

@mustaccio Estoy de acuerdo con usted, pero los artefactos apuntan en una dirección diferente. Si, de hecho, se produjo la reversión, el uso del archivo de registro debe volver a un número menor; y no permanecer en 99.3% cuando se lanza el mensaje de error.
ToC

1
Creo que la reversión toma espacio en el registro. Cuando el registro se llena durante la reversión, el DB se vuelve sospechoso. No se toca más. Esto parece una reversión instantánea, pero la reversión se ha aplazado hasta que pueda volver a poner la base de datos en línea (cuando haya espacio disponible en el disco) .; Además, cuando el registro se llena, SQL Server podría intentar hacer espacio mediante puntos de verificación que podrían explicar los picos en la actividad de E / S.
usr

Respuestas:


1

Como se indicó anteriormente, después de ejecutar más pruebas, llegué a conclusiones calculadas. Los resumí a todos en una publicación de blog aquí , pero copiaré algún contenido a esta publicación para la posteridad.

Conjetura (basada en algunas pruebas)

A partir de ahora, no tengo una explicación clara de por qué esto es así. Pero a continuación están mis estimaciones basadas en los artefactos reunidos durante las pruebas.

La reversión ocurre en ambos escenarios. Uno es la reversión explícita (el usuario presiona el botón Cancelar), el otro es implícito (el servidor SQL toma esa decisión internamente).

En ambos escenarios, el tráfico que va al archivo de registro es consistente. Vea las imágenes a continuación:

Escenario 1:

Escenario 1:

Escenario 2:

Escenario 2

  • Un artefacto que reforzó esta línea de pensamiento es capturar Sql Trace durante ambos escenarios.

    • El escenario 1 es evidente cuando presionamos 'Cancelar', retrocede.
    • En el Escenario 2, el mensaje de error se muestra después de realizar 'retroceder' implícitamente. En Sql Trace, vemos el mensaje de error "El registro de transacciones para la base de datos 'SampleDB' está lleno" mucho tiempo antes de que el mensaje se muestre en la pantalla. Entonces, supongo que las reversiones ocurren en ambos escenarios, pero el mensaje de error es que el Escenario 2 se muestra después de realizar la reversión de manera exitosa y completa.
  • El escenario 2 parece tomar más tiempo a medida que avanza mucho más, por lo que la reversión lleva más tiempo.

Comportamiento inexplicable:

  • ¿Por qué el uso del archivo de registro varía tanto?
    • Aumenta al 90%, luego baja al 85%, luego al 99% y permanece ahí durante mucho tiempo. Lo veo subir y bajar así varias veces: 99.2%, 99.8%, 99.1%, 99.7%. ¿Por qué pasó esto?
    • Una posible explicación es que puede haber un proceso en segundo plano (algo así como Log Flush) que limpia el archivo de registro cada pocos minutos. Y cada vez que se activa, se eliminan algunas entradas, lo que da como resultado más espacio libre disponible.

Cualquier idea para ayudar a explicar este comportamiento de una mejor manera es bienvenida.


2
Comprobado con Paul Randal, confirmó que llegó a la conclusión correcta: la reversión es la misma en ambos casos.
Paul White 9

0

Intenté el siguiente experimento y obtuve resultados similares. En ambos casos, fn_dblog () muestra una reversión que ocurre y parece suceder más rápido en el Escenario 2 que en el Escenario 1.

Por cierto, coloqué el MDF y el LDF en el mismo disco externo (USB 2.0).

Mi conclusión inicial es que no hay diferencia en la operación de reversión en este caso, y probablemente cualquier diferencia aparente de velocidad esté relacionada con el subsistema de E / S. Esa es solo mi hipótesis de trabajo en este momento.

Escenario 1:

  • Cree una base de datos con un archivo de registro que comience en 1 MB, crezca en fragmentos de 4 MB y tenga un tamaño máximo de 100 MB.
  • Abra una transacción explícita, ejecútela durante 10 segundos y luego cancélela manualmente dentro de SSMS
  • Mire el recuento de fn_dblog () y el tamaño de reserva de registro y consulte DBCC SQLPERF (LOGSPACE)

Escenario 2:

  • Cree una base de datos con un archivo de registro que comience en 1 MB, crezca en fragmentos de 4 MB y tenga un tamaño máximo de 100 MB.
  • Abra una transacción explícita, ejecútela hasta que el registro esté lleno. Aparece un error.
  • Mire el recuento de fn_dblog () y el tamaño de reserva de registro y consulte DBCC SQLPERF (LOGSPACE)

Resultados del monitor de rendimiento:

Escenario 1: ***Escenario 1***

Escenario 2: *** Escenario 2 ***

Código:

USE [maestro];
VAMOS

IF DATABASEPROPERTYEX (N'SampleDB ', N'Version')> 0
EMPEZAR
    ALTER DATABASE [SampleDB] SET SINGLE_USER
        CON ROLLBACK INMEDIATO;
    DROP DATABASE [SampleDB];
FINAL;
VAMOS

CREAR BASE DE DATOS [SampleDB] EN PRIMARIO 
( 
      NOMBRE = N'SampleDB '
    , FILENAME = N'E: \ data \ SampleDB.mdf ' 
    , TAMAÑO = 3MB 
    , FILEGROWTH = 1MB 
)
ACCEDER 
( 
      NOMBRE = N'SampleDB_log '
    , FILENAME = N'E: \ data \ SampleDB_log.ldf '
    , TAMAÑO = 1MB 
    , MAXSIZE = 100MB 
    , FILEGROWTH = 4MB 
);
VAMOS

USE [SampleDB];
VAMOS

- Agregar una tabla
CREAR TABLA dbo.test
(
    c1 CHAR (8000) NO NULL REPLICATE POR DEFECTO ('a', 8000)
) ENCENDIDO [PRIMARIO];
VAMOS

- Asegúrese de que no somos un modelo de recuperación pseudo-simple
BACKUP DATABASE SampleDB
TO DISK = 'NUL';
VAMOS

- Copia de seguridad del archivo de registro
REGISTRO DE COPIA DE SEGURIDAD SampleDB
TO DISK = 'NUL';
VAMOS

- Verifique el espacio de registro utilizado
DBCC SQLPERF (ESPACIO DE REGISTRO);
VAMOS

- ¿Cuántos registros son visibles con fn_dblog ()?
SELECCIONAR * DESDE fn_dblog (NULL, NULL); - Alrededor de las 9 en mi caso

/ **********************************
             ESCENARIO 1
********************************** /
- Abra una nueva transacción y luego retírela
COMIENZA LA TRANSACCIÓN

    INSERTAR EN dbo.test VALORES POR DEFECTO;
    GO 10000 - Let se ejecuta durante 10 segundos y luego presiona cancelar en la ventana de consulta SSMS

    - Cancelar la transacción
    - Debería tardar un par de segundos en terminar


- No es necesario revertir la transacción, ya que la cancelación ya lo hizo por usted.
-- Solo inténtalo. Obtendrás este error
- Mensaje 3903, Nivel 16, Estado 1, Línea 1
- La solicitud de TRANSACCIÓN DE ROLLBACK no tiene BEGIN TRANSACTION correspondiente.
TRANSACCIÓN EN ROLLBACK;

- ¿Cuál es el espacio de registro utilizado? Por encima del 100%.
DBCC SQLPERF (ESPACIO DE REGISTRO);
VAMOS

- ¿Cuántos registros son visibles con fn_dblog ()?
SELECCIONE * 
DE fn_dblog (NULL, NULL); - Alrededor de 91,926 en mi caso

- ¿Reserva de registro total mostrada por fn_dblog ()?
SELECCIONAR SUMA ([Reserva de registro]) AS [Reserva de registro total]
DE fn_dblog (NULL, NULL); - Alrededor de 88.72MB


/ **********************************
             ESCENARIO 2
********************************** /
- Sopla el DB y comienza de nuevo
USE [maestro];
VAMOS

IF DATABASEPROPERTYEX (N'SampleDB ', N'Version')> 0
EMPEZAR
    ALTER DATABASE [SampleDB] SET SINGLE_USER
        CON ROLLBACK INMEDIATO;
    DROP DATABASE [SampleDB];
FINAL;
VAMOS

CREAR BASE DE DATOS [SampleDB] EN PRIMARIO 
( 
      NOMBRE = N'SampleDB '
    , FILENAME = N'E: \ data \ SampleDB.mdf ' 
    , TAMAÑO = 3MB 
    , FILEGROWTH = 1MB 
)
ACCEDER 
( 
      NOMBRE = N'SampleDB_log '
    , FILENAME = N'E: \ data \ SampleDB_log.ldf '
    , TAMAÑO = 1MB 
    , MAXSIZE = 100MB 
    , FILEGROWTH = 4MB 
);
VAMOS

USE [SampleDB];
VAMOS

- Agregar una tabla
CREAR TABLA dbo.test
(
    c1 CHAR (8000) NO NULL REPLICATE POR DEFECTO ('a', 8000)
) ENCENDIDO [PRIMARIO];
VAMOS

- Asegúrese de que no somos un modelo de recuperación pseudo-simple
BACKUP DATABASE SampleDB
TO DISK = 'NUL';
VAMOS

- Copia de seguridad del archivo de registro
REGISTRO DE COPIA DE SEGURIDAD SampleDB
TO DISK = 'NUL';
VAMOS

- Ahora, explotemos el archivo de registro dentro de nuestra transacción
COMIENZA LA TRANSACCIÓN
    INSERTAR EN dbo.test VALORES POR DEFECTO;
    GO 10000

- La reversión nunca se dispara. Intentalo. Obtendrás un error.
- Mensaje 3903, Nivel 16, Estado 1, Línea 1
- La solicitud de TRANSACCIÓN DE ROLLBACK no tiene BEGIN TRANSACTION correspondiente.
TRANSACCIÓN EN ROLLBACK;

- ¿Está el archivo de registro 100% lleno? 
DBCC SQLPERF (ESPACIO DE REGISTRO);

- ¿Cuántos registros son visibles con fn_dblog ()?
SELECCIONE * 
DE fn_dblog (NULL, NULL); - Alrededor de 91,926 en mi caso
VAMOS

- ¿Reserva de registro total mostrada por fn_dblog ()?
SELECCIONAR SUMA ([Reserva de registro]) AS [Reserva de registro total]
DE fn_dblog (NULL, NULL); - 88.72MB
VAMOS

Gracias por las pruebas detalladas. Después de ejecutar más pruebas, llegué a una conclusión similar, así que escribí una publicación en el blog . Para mí, el Escenario 2 tarda más en revertirse porque la cantidad de trabajo realizado en el Escenario 2, antes de que Sql Server se dé cuenta de que la necesidad de revertir es mayor que el Escenario 1.
ToC
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.