解决方法
如果你正在考虑交易的每一个动作,而不仅仅是提交,我仍然认为你的陈述不正确.例如,排除外部因素,日志磁盘的速度与数据磁盘速度相比,事务所做的任何工作的回滚可能比首先进行的工作更快.
回滚正在读取更改的顺序文件并将其应用于内存中的数据页.原始的“工作”必须生成执行计划,获取页面,连接行等.
编辑:它取决于…
@JackDouglas指向this article,其中描述了回滚可能比原始操作花费更长时间的情况之一.这个例子是14小时的事务,不可避免地使用并行性,需要48小时才能回滚,因为回滚主要是单线程的.您很可能也会反复搅拌缓冲池,因此您不再将更改转换为内存中的页面.
所以,我之前的答案的修订版.回滚速度有多慢?考虑所有其他事项,对于典型的OLTP事务,它不是.在典型的范围之外,“撤消”可能需要更长的时间而不是“做”但是(这是一个潜在的绕口令?)为什么将取决于“做”是如何完成的.
编辑2:继续评论中的讨论,这是一个非常人为的例子,用于证明正在完成的工作是确定提交与回滚作为操作的相对费用的主要因素.
创建两个表并将其打包效率低下(每页浪费的空间):
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,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',SYSDATETIME()) GO
再做一遍,但发布并测量回滚.
DECLARE @StartTime DATETIME2,SYSDATETIME()) SET @StartTime = SYSDATETIME() ROLLBACK TRANSACTION SELECT 'Rollback',SYSDATETIME()) GO
@ Rows = 1我得到了一个相当一致的:
> 5500ms用于查找/更新
> 3ms提交
> 1ms回滚
@ Rows = 100时:
> 8500ms查找/更新
> 15ms提交
> 15ms回滚
@ Rows = 1000:
> 15000ms查找/更新
> 10ms提交
> 500ms回滚
回到原来的问题.如果你正在测量工作所花费的时间加上提交,那么回滚就会赢得大奖,因为大部分工作都花在查找要更新的行上,而不是实际修改数据.如果您正在单独查看提交操作,那么应该很清楚提交确实很少“工作”.承诺是“我做完了”.