ROLLBACK 是快速操作嗎?
RDBMS 系統是否針對
COMMIT
操作進行了優化?操作的速度有多慢/快ROLLBACK
,為什麼?
對於 SQL Server,您可能會爭辯說,送出操作只不過是將 LOP_COMMIT_XACT 寫入日誌文件並釋放鎖,這當然會比事務自 BEGIN TRAN 以來執行的每個操作的 ROLLBACK 更快。
如果您正在考慮事務的每個操作,而不僅僅是送出,我仍然認為您的陳述不正確。排除外部因素,例如日誌磁碟的速度與數據磁碟的速度相比,事務完成的任何工作的回滾都可能比首先完成的工作要快。
回滾是讀取更改的順序文件並將它們應用到記憶體數據頁。最初的“工作”必須生成執行計劃、獲取頁面、連接行等。
編輯:這取決於位…
@JackDouglas 指出這篇文章描述了回滾可能比原始操作花費更長的時間的情況之一。該範例是一個 14 小時的事務,不可避免地使用並行性,這需要 48 多個小時才能回滾,因為回滾主要是單執行緒的。您很可能還會反复攪動緩衝池,因此您不再需要反轉對記憶體頁面的更改。
所以,我之前的答案的修訂版。回滾慢了多少?考慮到所有其他因素,對於典型的 OLTP 事務來說,情況並非如此。在典型範圍之外,“撤消”可能比“做”花費更長的時間,但是(這是一個潛在的繞密碼嗎?)為什麼將取決於“做”是如何完成的。
Edit2:繼評論中的討論之後,這裡有一個非常人為的例子來證明正在完成的工作是確定送出與回滾作為操作的相對成本的主要因素。
創建兩個表並低效地打包它們(每頁浪費空間):
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
執行“錯誤”更新查詢,測量完成工作所花費的時間和發出送出所花費的時間。
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
再次執行相同操作,但發出並測量回滾。
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
使用 @Rows=1 我得到一個相當一致的結果:
- 查找/更新需要 5500 毫秒
- 3ms 送出
- 1ms回滾
使用@Rows=100:
- 8500ms 查找/更新
- 15 毫秒送出
- 15ms 回滾
使用@Rows=1000:
- 15000ms 查找/更新
- 10 毫秒送出
- 500ms 回滾
回到最初的問題。如果您要測量完成工作和送出所花費的時間,那麼回滾就是勝出,因為大部分工作都用於查找要更新的行,而不是實際修改數據。如果您正在孤立地查看送出操作,那麼應該清楚送出所做的“工作”很少。送出是“我完成了”。