ROLLBACK è un'operazione veloce?


Risposte:


14

Per SQL Server, potresti sostenere che un'operazione di commit non è altro che scrivere LOP_COMMIT_XACT nel file di registro e rilasciare i blocchi, che ovviamente sarà più veloce del ROLLBACK di ogni azione eseguita dalla transazione da BEGIN TRAN.

Se stai considerando ogni azione di una transazione, non solo il commit, direi comunque che la tua affermazione non è vera. Escludendo fattori esterni, ad esempio la velocità del disco di registro rispetto alla velocità del disco di dati, è probabile che il rollback di qualsiasi lavoro svolto da una transazione sia più veloce di quello che fa il lavoro in primo luogo.

Un rollback sta leggendo un file sequenziale di modifiche e le applica a pagine di dati in memoria. Il "lavoro" originale doveva generare un piano di esecuzione, acquisire pagine, unire righe ecc.

Modifica: il bit dipende ...

@JackDouglas ha sottolineato questo articolo che descrive una delle situazioni in cui il rollback può richiedere molto più tempo dell'operazione originale. L'esempio è una transazione di 14 ore, che utilizza inevitabilmente il parallelismo, che richiede più di 48 ore per il rollback poiché il rollback è principalmente a thread singolo. Molto probabilmente dovresti sfornare ripetutamente il pool di buffer, quindi non stai più invertendo le modifiche alle pagine in memoria.

Quindi, una versione rivista della mia risposta precedente. Quanto è più lento il rollback? Tutto considerato, per una tipica transazione OLTP non lo è. Al di fuori dei limiti del tipico, può essere necessario più tempo per "annullare" che per "fare" ma (è un potenziale scioglilingua?) Perché dipenderà da come è stato fatto il "fare".

Edit2: a seguito della discussione nei commenti, ecco un esempio molto ingegnoso per dimostrare che il lavoro svolto è il fattore principale nel determinare la spesa relativa del commit rispetto al rollback come operazioni.

Crea due tabelle e impacchettale in modo inefficiente (spazio sprecato per pagina):

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

Eseguire una query di aggiornamento "errata", misurando il tempo impiegato per eseguire il lavoro e il tempo impiegato per eseguire il commit.

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

Fai di nuovo lo stesso, ma emetti e misura il rollback.

    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 ottengo un valore ragionevolmente coerente:

  • 5500 ms per la ricerca / aggiornamento
  • 3ms commit
  • Rollback di 1 ms

Con @ Rows = 100:

  • 8500ms trova / aggiorna
  • 15ms di commit
  • Rollback di 15ms

Con @ Rows = 1000:

  • 15000ms trova / aggiorna
  • Impegno 10ms
  • Rollback di 500ms

Torna alla domanda originale. Se stai misurando il tempo impiegato per eseguire il lavoro più il commit, il rollback sta vincendo a mani basse perché la maggior parte di quel lavoro viene impiegata per trovare la riga da aggiornare, non modificando effettivamente i dati. Se stai osservando l'operazione di commit in isolamento, dovrebbe essere chiaro che il commit fa pochissimo "lavoro" in quanto tale. Commit è "Ho finito".


2
"Meno lavoro" non è necessariamente "più veloce"
Jack Douglas,

Sapevo che begin tranaumenta solo il contatore delle transazioni. Se ti ho capito, rdbms sta facendo tutti i compiti (unisce le righe, genera piani di esecuzione ...) su COMMIT?
Garik,

3
No, tutto il lavoro viene svolto prima del commit. L'operazione stessa di commit fa relativamente poco.
Mark Storey-Smith,

@Mark Ho fatto alcuni test approssimativi e pronti inserendo 2m di righe e sia in fase di commit che di rollback. Il tempo complessivo compreso il rollback variava da 10 a 30 secondi, contro tra 6 e 14 secondi per il tempo complessivo compreso il commit. YMMV ovviamente, ma questo indica che il rollback del ballpark è quasi altrettanto lungo o più lungo della transazione originale almeno nel mio ambiente.
Jack Douglas,

2
Se dovessi misurare il tempo per il completamento dell'operazione di commit, mi aspetto che sia minimo a meno che non venga emesso contemporaneamente un checkpoint (che è separato e non correlato). Questo è il mio punto, il commit fa pochissimo mentre il rollback fa tutto ciò che è accaduto prima del commit più un po 'di più. La varianza nei tuoi test suggerisce altri fattori in gioco, ma cercherò sicuramente di mettere insieme alcuni script in seguito.
Mark Storey-Smith,

13

Per Oracle, il rollback può richiedere molte volte più tempo del tempo necessario per apportare le modifiche che stanno eseguendo il rollback. Questo spesso non importa perché

  1. Nessun blocco viene trattenuto durante il rollback della transazione
  2. È gestito da un processo in background a bassa priorità

Per SQL Server non sono sicuro che la situazione sia la stessa, ma qualcun altro dirà se non è ...

Per quanto riguarda "perché", direi che rollbackdovrebbe essere raro , di solito solo se qualcosa è andato storto, e ovviamente commitè probabile che sia molto più comune - quindi ha senso ottimizzare percommit


9

Il rollback non è solo "oh, non importa" - in molti casi deve davvero annullare ciò che aveva già fatto. Non esiste alcuna regola secondo cui l'operazione di rollback sarà sempre più lenta o sempre più rapida dell'operazione originale, sebbene anche se la transazione originale fosse eseguita in parallelo, il rollback è a thread singolo. Se stai aspettando, ti suggerisco che è più sicuro continuare ad aspettare.

Tutto questo cambia con SQL Server 2019, ovviamente, e Accelerated Database Recovery (che, a una penalità che è anche variabile, consente il rollback istantaneo indipendentemente dalla dimensione dei dati).


2
E tutti abbiamo avuto quella conversazione "ci vogliono anni per il rollback, riavvialo" a un certo punto giusto?
Mark Storey-Smith,

Ho visto molti clienti farlo. Alcuni escono relativamente incolumi, altri sono molto meno fortunati.
Aaron Bertrand

1
@ MarkStorey-Smith - Se si riavvia mid-rollback, SQL Server non deve comunque continuare il suo rollback all'avvio?
Nick Chammas,

2
@Nick dipende: se il rollback è stato bloccato prima del riavvio, ad esempio, potrebbe comportarsi molto più rapidamente dopo il riavvio del servizio perché quell'altro processo è stato appena interrotto. C'è un sacco di "what if" in questo scenario: ogni volta che riavvii un server o riavvii un servizio per "risolvere" un problema, probabilmente ci sono alcuni problemi molto più seri in gioco.
Aaron Bertrand

2
@Nick, sì, è esattamente quello che succede. Il mio commento era destinato a essere "ironico", in modo tale che inevitabilmente si finisce per doverlo spiegare al grilletto gente felice che vuole colpire il riavvio ogni volta che qualcosa non si comporta come previsto.
Mark Storey-Smith,

8

Non tutte le transazioni avranno la loro attività di commit molto meglio del loro rollback. Uno di questi casi è l'operazione di eliminazione in SQL. Quando una transazione elimina le righe, queste righe vengono contrassegnate come record fantasma. Una volta emesso un commit e avvia un'attività di pulizia dei record fantasma, solo questi record vengono "cancellati".

Se invece è stato emesso un rollback, rimuove solo i segni fantasma da questi record e non le istruzioni di inserimento intensivo.


Un buon esempio di come alcune operazioni sono ottimizzate per il rollback.
Mark Storey-Smith,

5

Non tutti lo sono. PostgreSQL non impiega più tempo a tornare indietro che a impegnarsi poiché le due operazioni sono effettivamente identiche in termini di I / O del disco. In realtà non penso che questa sia una questione di ottimizzazione per il commit, in quanto si tratta di quali altre query si stanno ottimizzando.

La domanda di base è come affrontare il layout su disco e in che modo influisce sul commit rispetto al rollback. I principali db che eseguono il rollback più lentamente rispetto al commit tendono a spostare i dati, in particolare dalle tabelle raggruppate, fuori dalle strutture di dati principali e inserirli in un segmento di rollback durante l'aggiornamento dei dati. Ciò significa che per impegnarti è sufficiente eliminare il segmento di rollback, ma per eseguire il rollback è necessario copiare tutti i dati.

Per PostgreSQL, tutte le tabelle sono tabelle heap e gli indici sono separati. Ciò significa che quando si esegue il rollback o il commit, non è necessario riorganizzare i dati. Questo rende il commit e il rollback sia veloci.

Tuttavia, rende alcune altre cose un po 'più lente. Una ricerca di chiave primaria, ad esempio, deve attraversare un file indice e quindi deve colpire la tabella heap (supponendo che non vi siano indici di copertura applicabili). Questo non è un grosso problema, ma aggiunge una ricerca di pagine extra o forse anche alcune ricerche di pagine casuali (se si sono verificati molti aggiornamenti su quella riga) per verificare altre informazioni e visibilità.

La velocità qui, tuttavia, non è una questione di ottimizzazione in PostgreSQL per operazioni di scrittura rispetto a quelle di lettura. È riluttante privilegiare alcune operazioni di lettura su altre. Di conseguenza PostgreSQL si comporta in media, come pure gli altri db. Sono solo alcune operazioni che possono essere più veloci o più lente.

Quindi penso che la risposta effettiva sia che i db sono ottimizzati per determinati carichi di lavoro sul lato di lettura e questo porta a sfide sul lato di scrittura. Di solito dove c'è una domanda, gli commit sono generalmente, sebbene non sempre, favoriti dai rollback. Questo tuttavia dipende dalle implicazioni del fare uno dei due (gli aggiornamenti sono diversi dalle eliminazioni).


Buona risposta, ma un piccolo cavillo: "Per PostgreSQL, tutte le tabelle sono tabelle heap e gli indici sono separati. Ciò significa che quando si esegue il rollback o il commit, nessun dato deve essere riorganizzato" non è questo il motivo per cui nessun dato deve essere riorganizzato, piuttosto perché "I principali db che eseguono il rollback più lentamente rispetto al commit tendono a spostare i dati", e pag no, come hai detto. Per impostazione predefinita Oracle utilizza anche l'archiviazione dell'heap: la differenza principale è che Oracle utilizza "annulla" e recupera tutto lo spazio su commit / rollback piuttosto che seguire il percorso "vuoto".
Jack Douglas
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.