Risposte:
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:
Con @ Rows = 100:
Con @ Rows = 1000:
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".
begin tran
aumenta solo il contatore delle transazioni. Se ti ho capito, rdbms sta facendo tutti i compiti (unisce le righe, genera piani di esecuzione ...) su COMMIT?
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é
Per SQL Server non sono sicuro che la situazione sia la stessa, ma qualcun altro dirà se non è ...
Per quanto riguarda "perché", direi che rollback
dovrebbe essere raro , di solito solo se qualcosa è andato storto, e ovviamente commit
è probabile che sia molto più comune - quindi ha senso ottimizzare percommit
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).
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.
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).