In che modo differiscono questi due rollback di SQL Server?


13

In SQL Server 2008 R2, in che modo differiscono questi due rollback:

  1. Esegui una ALTERdichiarazione, per alcuni minuti, quindi premi "Annulla esecuzione". Sono necessari alcuni minuti per eseguire il rollback completo.

  2. Esegui la stessa ALTERistruzione, ma assicurati che il LDFfile non sia abbastanza grande per essere completato correttamente. Una volta raggiunto il LDFlimite e non è consentita la "crescita automatica", l'esecuzione della query si interrompe immediatamente (o si verifica un rollback) con questo messaggio di errore:

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

In che modo questi due sono diversi sui seguenti punti?

  1. Perché il secondo "rollback" è istantaneo? Non sono del tutto sicuro che si possa chiamare rollback. La mia ipotesi è che il registro delle transazioni sia scritto man mano che l'esecuzione procede e una volta che si rende conto che non c'è abbastanza spazio per completare completamente l'attività, si ferma solo con un messaggio 'end', senza commit.

  2. Cosa succede quando il primo rollback richiede così tanto tempo (un rollback è a thread singolo)?
    2.1. SQL Server torna indietro e annulla le voci create nel LDFfile?
    2.2. La LDFdimensione del file si riduce alla fine del rollback (da DBCC SQLPERF(LOGSPACE))

  3. Un'ulteriore domanda: durante il secondo scenario, SQL Server inizia a consumare i LDFfile abbastanza rapidamente. Nel mio caso, è aumentato dal 18% di utilizzo al 90% di utilizzo nei primi minuti (<4 minuti). Ma una volta raggiunto il 99%, è rimasto lì per altri 8 minuti, mentre ha oscillato l'utilizzo tra il 99,1% e il 99,8%. Va su (99,8%) e giù (99,2%) e su (99,7%) e giù (99,5%) alcune volte prima che l'errore venga generato. Cosa sta succedendo dietro le quinte?

Tutti i collegamenti MSDN che potrebbero aiutare a spiegarlo di più sono apprezzati.

Su suggerimento di Ali Razeghi, sto aggiungendo perfmon: Disk Bytes/sec

Scenario 1:

scenario 1

Scenario 2:

Scenario 2


Solo un breve commento: dimensione del file! = Spazio utilizzato all'interno di un file.
Aaron Bertrand

@Aaron Sì, ne ho familiarità. Ho usato DBCC SQLPERF (LOGSPACE) per misurare l'utilizzo. Il file LDF è rimasto lo stesso per tutta la durata, poiché ho limitato la dimensione del file a 10 GB. L'uso interno varia.
ToC

1
Sospetto che il secondo rollback appaia istantaneo, poiché l'errore viene segnalato dopo il completamento del rollback. Questi sono probabilmente gli 8 minuti che osservi in ​​3., in cui l'utilizzo di LDF rimane piuttosto costante.
Mustaccio

@mustaccio Concordo con te ma i manufatti puntano in una direzione diversa. Se in effetti, si è verificato il rollback, l'utilizzo del file di registro deve essere riportato a un numero inferiore; e non rimanere al 99,3% quando viene generato il messaggio di errore.
ToC

1
Credo che il rollback occupi spazio nel registro. Quando il registro si riempie durante il rollback, il DB diventa sospetto. Non viene più toccato. Sembra un rollback istantaneo ma il rollback è stato rinviato fino a quando non si è in grado di riportare il database online (quando è disponibile spazio su disco) .; Inoltre, quando il registro si riempie, SQL Server potrebbe tentare di liberare spazio controllando il punto che potrebbe spiegare picchi di attività IO.
usr

Risposte:


1

Come indicato sopra, dopo aver eseguito più test, sono arrivato a conclusioni calcolate. Li ho riassunti tutti in un post sul blog qui , ma copierò alcuni contenuti su questo post per i posteri.

Congettura (basata su alcuni test)

A partire da ora, non ho una spiegazione chiara del perché. Ma di seguito sono riportate le mie stime basate sugli artefatti raccolti durante i test.

Il rollback si verifica in entrambi gli scenari. Uno è il rollback esplicito (l'utente che preme il pulsante Annulla), l'altro è implicito (il server SQL prende quella decisione internamente).

In entrambi gli scenari, il traffico diretto al file di registro è coerente. Vedi le immagini qui sotto:

Scenario 1:

Scenario 1:

Scenario 2:

Scenario 2

  • Un artefatto che ha rafforzato questa linea di pensiero è catturare la Traccia Sql in entrambi gli scenari.

    • Lo scenario 1 è evidente anche quando si preme 'Annulla', torna indietro.
    • Nello scenario 2, il messaggio di errore viene visualizzato dopo aver eseguito implicitamente il "rollback". In Traccia SQL, viene visualizzato il messaggio di errore "Il registro delle transazioni per il database" SampleDB "è pieno" molto tempo prima che il messaggio venga visualizzato sullo schermo. Quindi, la mia ipotesi è che i rollback si verificano in entrambi gli scenari, ma il messaggio di errore è Scenario 2 viene visualizzato dopo aver eseguito correttamente e completamente il rollback.
  • Lo scenario 2 sembra richiedere più tempo man mano che avanza molto più avanti, quindi il rollback richiede più tempo.

Comportamento inspiegabile:

  • Perché l'utilizzo del file di registro varia così tanto?
    • Aumenta al 90%, quindi all'85%, quindi fino al 99% e vi si ferma a lungo. Lo vedo andare su e giù in questo modo diverse volte: 99,2%, 99,8%, 99,1%, 99,7%. Perché succede?
    • Una possibile spiegazione è che potrebbe esserci un processo in background (qualcosa come Log Flush) che pulisce il file di registro ogni pochi minuti. E ogni volta che entra in gioco, alcune voci vengono cancellate, con conseguente maggiore spazio libero disponibile.

Qualsiasi idea per aiutare a spiegare meglio questo comportamento è benvenuta.


2
Controllato con Paul Randal, ha confermato che sei giunto alla conclusione corretta: il rollback è lo stesso in entrambi i casi.
Paul White 9

0

Ho provato il seguente esperimento e ho ottenuto risultati simili. In entrambi i casi, fn_dblog () mostra il rollback che si verifica e sembra accadere più velocemente nello scenario 2 che nello scenario 1.

A proposito, ho posizionato sia MDF che LDF sullo stesso singolo disco esterno (USB 2.0).

La mia conclusione iniziale è che in questo caso non vi è alcuna differenza nel funzionamento del rollback e probabilmente qualsiasi differenza di velocità apparente è correlata al sottosistema I / O. Questa è solo la mia ipotesi di lavoro al momento.

Scenario 1:

  • Creare un database con un file di registro che inizia a 1 MB, cresce in blocchi di 4 MB e ha una dimensione massima di 100 MB.
  • Aprire una transazione esplicita, eseguirla per 10 secondi, quindi annullarla manualmente in SSMS
  • Guarda il conteggio fn_dblog () e registra la dimensione della riserva e controlla DBCC SQLPERF (LOGSPACE)

Scenario 2:

  • Creare un database con un file di registro che inizia a 1 MB, cresce in blocchi di 4 MB e ha una dimensione massima di 100 MB.
  • Aprire una transazione esplicita, eseguirla fino a quando non viene visualizzato l'errore completo del registro
  • Guarda il conteggio fn_dblog () e registra la dimensione della riserva e controlla DBCC SQLPERF (LOGSPACE)

Risultati di Performance Monitor:

Scenario 1: ***Scenario 1***

Scenario 2: *** Scenario 2 ***

Codice:

USE [master];
PARTIRE

SE DATABASEPROPERTYEX (N'SampleDB ', N'Version')> 0
INIZIO
    ALTER DATABASE [SampleDB] SET SINGLE_USER
        CON ROLLBACK IMMEDIATO;
    DROP DATABASE [SampleDB];
FINE;
PARTIRE

CREA DATABASE [SampleDB] SU PRIMARY 
( 
      NAME = N'SampleDB '
    , FILENAME = N'E: \ data \ SampleDB.mdf ' 
    , DIMENSIONE = 3 MB 
    , FILEGROWTH = 1 MB 
)
ACCEDERE 
( 
      NAME = N'SampleDB_log '
    , FILENAME = N'E: \ data \ SampleDB_log.ldf '
    , DIMENSIONE = 1 MB 
    , MAXSIZE = 100 MB 
    , FILEGROWTH = 4 MB 
);
PARTIRE

USE [SampleDB];
PARTIRE

- Aggiungi una tabella
CREA TABELLA dbo.test
(
    c1 CHAR (8000) NON NULL DEFAULT REPLICATE ('a', 8000)
) SU [PRIMARIO];
PARTIRE

- Assicurarsi che non lo siamo è un modello di recupero pseudo-semplice
DATABASE DI BACKUP SampleDB
TO DISK = 'NUL';
PARTIRE

- Eseguire il backup del file di registro
BACKUP LOG SampleDB
TO DISK = 'NUL';
PARTIRE

- Controllare lo spazio del registro utilizzato
DBCC SQLPERF (LOGSPACE);
PARTIRE

- Quanti record sono visibili con fn_dblog ()?
SELEZIONA * DA fn_dblog (NULL, NULL); - Circa 9 nel mio caso

/ **********************************
             SCENARIO 1
********************************** /
- Aprire una nuova transazione e quindi ripristinarla
INIZIA TRANSAZIONE

    INSERISCI SU dbo.test VALORI PREDEFINITI;
    GO 10000: lasciare funzionare per 10 secondi, quindi premere Annulla nella finestra delle query SSMS

    - Annulla la transazione
    - Dovrebbero essere necessari alcuni secondi per terminare


- Non è necessario eseguire il rollback della transazione, poiché l'annullamento l'ha già fatto per te.
- Provaci. Otterrai questo errore
- Messaggio 3903, livello 16, stato 1, riga 1
- La richiesta TRANSAZIONE ROLLBACK non ha OPERAZIONI INIZIALI corrispondenti.
TRANSAZIONE ROLLBACK;

- Qual è lo spazio di registro utilizzato? Superiore al 100%
DBCC SQLPERF (LOGSPACE);
PARTIRE

- Quanti record sono visibili con fn_dblog ()?
SELEZIONARE * 
FROM fn_dblog (NULL, NULL); - Circa 91.926 nel mio caso

- Riserva di registro totale mostrata da fn_dblog ()?
SELECT SUM ([Log Reserve]) AS [Total Log Reserve]
FROM fn_dblog (NULL, NULL); - Circa 88,72 MB


/ **********************************
             SCENARIO 2
********************************** /
- Soffiare via il DB e ricominciare
USE [master];
PARTIRE

SE DATABASEPROPERTYEX (N'SampleDB ', N'Version')> 0
INIZIO
    ALTER DATABASE [SampleDB] SET SINGLE_USER
        CON ROLLBACK IMMEDIATO;
    DROP DATABASE [SampleDB];
FINE;
PARTIRE

CREA DATABASE [SampleDB] SU PRIMARY 
( 
      NAME = N'SampleDB '
    , FILENAME = N'E: \ data \ SampleDB.mdf ' 
    , DIMENSIONE = 3 MB 
    , FILEGROWTH = 1 MB 
)
ACCEDERE 
( 
      NAME = N'SampleDB_log '
    , FILENAME = N'E: \ data \ SampleDB_log.ldf '
    , DIMENSIONE = 1 MB 
    , MAXSIZE = 100 MB 
    , FILEGROWTH = 4 MB 
);
PARTIRE

USE [SampleDB];
PARTIRE

- Aggiungi una tabella
CREA TABELLA dbo.test
(
    c1 CHAR (8000) NON NULL DEFAULT REPLICATE ('a', 8000)
) SU [PRIMARIO];
PARTIRE

- Assicurarsi che non lo siamo è un modello di recupero pseudo-semplice
DATABASE DI BACKUP SampleDB
TO DISK = 'NUL';
PARTIRE

- Eseguire il backup del file di registro
BACKUP LOG SampleDB
TO DISK = 'NUL';
PARTIRE

- Ora, facciamo saltare il file di registro all'interno della nostra transazione
INIZIA TRANSAZIONE
    INSERISCI SU dbo.test VALORI PREDEFINITI;
    GO 10000

- Il rollback non si attiva mai. Provalo. Riceverai un errore.
- Messaggio 3903, livello 16, stato 1, riga 1
- La richiesta TRANSAZIONE ROLLBACK non ha OPERAZIONI INIZIALI corrispondenti.
TRANSAZIONE ROLLBACK;

- Il file di registro è pieno al 100%? 
DBCC SQLPERF (LOGSPACE);

- Quanti record sono visibili con fn_dblog ()?
SELEZIONARE * 
FROM fn_dblog (NULL, NULL); - Circa 91.926 nel mio caso
PARTIRE

- Riserva di registro totale mostrata da fn_dblog ()?
SELECT SUM ([Log Reserve]) AS [Total Log Reserve]
FROM fn_dblog (NULL, NULL); - 88,72 MB
PARTIRE

Grazie per i test dettagliati. Dopo aver eseguito altri test, sono arrivato a una conclusione simile, quindi ho scritto un post sul blog . Per me lo Scenario 2 richiede più tempo per il rollback perché la quantità di lavoro eseguita nello Scenario 2, prima che Sql Server si renda conto della necessità di eseguire il rollback è maggiore dello Scenario 1.
ToC
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.