这两个SQL Server回滚有何不同?


13

在SQL Server 2008 R2中,这两个回滚有何不同:

  1. 运行ALTER语句几分钟,然后单击“取消执行”。完全回滚需要几分钟。

  2. 运行相同的ALTER语句,但是请确保该LDF文件的大小不足以使其成功完成。一旦达到LDF限制并且不允许“自动增长”,查询执行将立即停止(或发生回滚),并显示以下错误消息:

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

这两个方面在以下几点上有何不同?

  1. 为什么第二个“回滚”是瞬时的?我不确定是否可以将其称为回滚。我的猜测是,事务日志是在执行过程中编写的,一旦它意识到没有足够的空间来完全完成任务,它就会以一些“结束”消息停止,而不会提交。

  2. 第一次回滚需要很多时间(回滚是单线程的)会发生什么?
    2.1。SQL Server会返回并撤消LDF文件中的输入吗?
    2.2。该LDF文件大小在回滚结束变得更小(从DBCC SQLPERF(LOGSPACE)

  3. 另一个问题:在第二种情况下,SQL Server开始LDF很快消耗文件。就我而言,在最初的几分钟(<4分钟)内,使用率从18%增加到90%。但一旦达到99%,它会再呆在那里8分钟,而使用量则在99.1%至99.8%之间波动。在引发错误之前,它多次上升(99.8%)和下降(99.2%),再次上升(99.7%)和下降(99.5%)。幕后发生了什么?

感谢任何可以帮助解释这一点的MSDN链接。

在Ali Razeghi的建议下,我添加了perfmon: Disk Bytes/sec

方案1:

场景1

方案2:

方案2


快速评论一下:文件大小!=文件内使用的空间。
亚伦·伯特兰

@Aaron是的,我对此很熟悉。我使用DBCC SQLPERF(LOGSPACE)来衡量使用情况。LDF文件在整个过程中保持不变,因为我将文件大小限制为10 GB。内部用法各不相同。
ToC

1
我怀疑是第二回退出现,因为报告错误瞬间,回滚完成。这些可能是您在3.中观察到的8分钟,其中LDF的使用保持不变。
mustaccio

@mustaccio我会同意你的观点,但是工件指向不同的方向。如果实际上发生了回滚,则必须将日志文件的使用率还原为较小的值;并在抛出错误消息时不停留在99.3%。
ToC

1
我相信回滚会占用日志空间。在回滚期间日志满时,数据库将变为可疑状态。不再赘述。看起来像是瞬时回滚,但是回滚已推迟到能够使数据库恢复联机(有可用磁盘空间时)为止。同样,当日志已满时,SQL Server可能会尝试通过检查点腾出空间,这可能解释了IO活动的激增。
usr

Answers:


1

如上所述,在运行更多测试之后,我得出了一个计算得出的结论。我将所有内容汇总到此处博客文章中,但为了后代,我会将一些内容复制到这篇文章中。

猜想(基于一些测试)

到目前为止,我还不清楚为什么会这样。但是,以下是我根据测试期间收集的工件得出的估计。

两种情况下都会发生回滚。一种是显式回滚(用户单击“取消”按钮),另一种是隐式的(Sql Server在内部做出决定)。

在这两种情况下,去往日志文件的流量都是一致的。参见下面的图片:

方案1:

方案1:

方案2:

方案2

  • 增强了这种思路的一种工件是在两种情况下都捕获Sql Trace。

    • 场景1是不言而喻的,当我们点击“取消”时,它会回滚。
    • 在方案2中,隐式执行“回滚”后将显示错误消息。在Sql Trace中,很长一段时间之前,我们都会在屏幕上显示错误消息“数据库'SampleDB'的事务日志已满”。因此,我的猜测是在两种情况下都会发生回滚,但是错误消息是在成功且完全执行回滚后显示了方案2。
  • 随着方案2的进一步发展,方案2似乎需要更长的时间,因此回滚需要更长的时间。

无法解释的行为:

  • 为什么日志文件的使用差异如此之大?
    • 它增加到90%,然后下降到85%,然后上升到99%,并在那里徘徊了很长时间。我几次看到它像这样上下波动:99.2%,99.8%,99.1%,99.7%。为什么会这样?
    • 一种可能的解释是,可能有一个后台进程(类似于Log Flush)每隔几分钟便会清理一次日志文件。每次启动时,都会清除一些条目,从而提供更多可用空间。

我们欢迎任何有助于更好地解释这种行为的想法。


2
经过Paul Randal的检查,他确认您得出了正确的结论-在两种情况下回滚都是相同的。
保罗·怀特9

0

我尝试了以下实验,并获得了相似的结果。在这两种情况下,fn_dblog()都显示发生回滚,并且在方案2中它似乎比在方案1中更快。

顺便说一下,我将MDF和LDF都放在了同一个外部(USB 2.0)磁盘上。

我的初步结论是,在这种情况下,回滚操作没有差异,并且可能任何明显的速度差异都与I / O子系统有关。这只是我目前的假设。

方案1:

  • 创建一个数据库文件,其日志文件的大小为1MB,以4MB的块增长,最大大小为100MB。
  • 打开一个显式事务,运行它10秒钟,然后在SSMS中手动取消它
  • 查看fn_dblog()计数和日志保留大小,并检查DBCC SQLPERF(LOGSPACE)

方案2:

  • 创建一个数据库文件,其日志文件的大小为1MB,以4MB的块增长,最大大小为100MB。
  • 打开一个显式事务,运行它,直到显示日志已满错误
  • 查看fn_dblog()计数和日志保留大小,并检查DBCC SQLPERF(LOGSPACE)

性能监视器结果:

方案1: ***方案1 ***

方案2: ***方案2 ***

码:

USE [master];
走

IF DATABASEPROPERTYEX(N'SampleDB',N'Version')> 0
开始
    ALTER DATABASE [SampleDB] SET SINGLE_USER
        立即回滚;
    DROP DATABASE [SampleDB];
结束;
走

在主数据库上创建数据库[SampleDB] 
( 
      NAME = N'SampleDB'
    ,FILENAME = N'E:\ data \ SampleDB.mdf' 
    ,大小= 3MB 
    ,文件增长= 1MB 
)
登入 
( 
      NAME = N'SampleDB_log'
    ,FILENAME = N'E:\ data \ SampleDB_log.ldf'
    ,大小= 1MB 
    ,MAXSIZE = 100MB 
    ,FILEGROWTH = 4MB 
);
走

USE [SampleDB];
走

-添加表格
创建表dbo.test
(
    c1 CHAR(8000)非空默认副本('a',8000)
)在[PRIMARY]上;
走

-确保我们不是伪简单的恢复模型
备份数据库SampleDB
TO DISK ='NUL';
走

-备份日志文件
备份日志SampleDB
TO DISK ='NUL';
走

-检查已用的日志空间
DBCC SQLPERF(LOGSPACE);
走

-使用fn_dblog()可以看到多少条记录?
SELECT * FROM fn_dblog(NULL,NULL); -在我的情况下大约9

/ **************************************
             场景1
************************************** /
-打开一个新交易,然后回滚
开始交易

    插入dbo.test默认值;
    GO 10000-让其运行10秒钟,然后在SSMS查询窗口中单击``取消''

    -取消交易
    -需要几秒钟才能完成


-无需回滚交易,因为取消已经为您完成了。
-  去尝试一下。您会收到此错误
-消息3903,第16级,状态1,第1行
-ROLLBACK TRANSACTION请求没有相应的BEGIN TRANSACTION。
回滚交易;

-使用的日志空间是多少?高于100%。
DBCC SQLPERF(LOGSPACE);
走

-使用fn_dblog()可以看到多少条记录?
选择 * 
从fn_dblog(NULL,NULL); -就我而言大约91,926

-由fn_dblog()显示的总日志储备?
SELECT SUM([日志保留])AS [总日志保留]
从fn_dblog(NULL,NULL); -约88.72MB


/ **************************************
             场景2
************************************** /
-吹走数据库并重新开始
USE [master];
走

IF DATABASEPROPERTYEX(N'SampleDB',N'Version')> 0
开始
    ALTER DATABASE [SampleDB] SET SINGLE_USER
        立即回滚;
    DROP DATABASE [SampleDB];
结束;
走

在主数据库上创建数据库[SampleDB] 
( 
      NAME = N'SampleDB'
    ,FILENAME = N'E:\ data \ SampleDB.mdf' 
    ,大小= 3MB 
    ,文件增长= 1MB 
)
登入 
( 
      NAME = N'SampleDB_log'
    ,FILENAME = N'E:\ data \ SampleDB_log.ldf'
    ,大小= 1MB 
    ,MAXSIZE = 100MB 
    ,FILEGROWTH = 4MB 
);
走

USE [SampleDB];
走

-添加表格
创建表dbo.test
(
    c1 CHAR(8000)非空默认副本('a',8000)
)在[PRIMARY]上;
走

-确保我们不是伪简单的恢复模型
备份数据库SampleDB
TO DISK ='NUL';
走

-备份日志文件
备份日志SampleDB
TO DISK ='NUL';
走

-现在,让我们在事务中删除日志文件
开始交易
    插入dbo.test默认值;
    去10000

-永不触发回滚。尝试一下。您会得到一个错误。
-消息3903,第16级,状态1,第1行
-ROLLBACK TRANSACTION请求没有相应的BEGIN TRANSACTION。
回滚交易;

-日志文件是否100%已满? 
DBCC SQLPERF(LOGSPACE);

-使用fn_dblog()可以看到多少条记录?
选择 * 
从fn_dblog(NULL,NULL); -就我而言大约91,926
走

-由fn_dblog()显示的总日志储备?
SELECT SUM([日志保留])AS [总日志保留]
从fn_dblog(NULL,NULL); -88.72MB
走

感谢您的详细测试。经过更多测试后,我得出了类似的结论,因此我写了一篇博客文章。对我来说,方案2需要更长的回滚时间,因为在方案2中执行的工作量比Sql Server早于方案
1。– ToC
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.