ToC*_*ToC 13 sql-server sql-server-2008-r2 database-internals rollback transaction-log
在 SQL Server 2008 R2 中,这两种回滚有何不同:
运行一条ALTER
语句几分钟,然后点击“取消执行”。完全回滚需要几分钟时间。
运行相同的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
Run Code Online (Sandbox Code Playgroud)
这两者在以下几点上有何不同?
为什么第二次“回滚”是瞬时的?我不完全确定它是否可以称为回滚。我的猜测是,事务日志是随着执行的进行而写入的,一旦它意识到没有足够的空间来完全完成任务,它就会停止并显示一些“结束”消息,而不提交。
当第一次回滚花费这么多时间(回滚单线程)时会发生什么?
2.1. SQL Server 会返回并撤消LDF
文件中的条目吗?
2.2. 该LDF
文件大小在回滚结束变得更小(从DBCC SQLPERF(LOGSPACE)
)
另一个问题:在第二种情况下,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
我尝试了以下实验并得到了类似的结果。在这两种情况下,fn_dblog() 都显示正在发生回滚,并且场景 2 中的回滚似乎比场景 1 中发生得更快。
顺便说一下,我将 MDF 和 LDF 放置在同一个外部 (USB 2.0) 磁盘上。
我的初步结论是,在这种情况下回滚操作没有差异,任何明显的速度差异可能都与 I/O 子系统有关。这只是我目前的工作假设。
场景一:
场景2:
性能监控结果:
代码:
使用[主控]; 去 IF DATABASEPROPERTYEX (N'SampleDB', N'版本') > 0 开始 更改数据库 [SampleDB] 设置单用户 立即回滚; 删除数据库[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 , 最大大小 = 100MB , 文件增长 = 4MB ); 去 使用[SampleDB]; 去 -- 添加一个表 创建表 dbo.test ( c1 CHAR(8000) NOT NULL 默认复制('a',8000) ) 在[主要]; 去 -- 确保我们不是伪简单的恢复模型 备份数据库 SampleDB 到磁盘='NUL'; 去 -- 备份日志文件 备份日志SampleDB 到磁盘='NUL'; 去 -- 检查已使用的日志空间 DBCC SQLPERF(日志空间); 去 -- 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(日志空间); 去 -- fn_dblog() 有多少条记录可见? 选择 * FROM fn_dblog(NULL,NULL); -- 以我为例,大约为 91,926 -- fn_dblog() 显示的总日志保留? SELECT SUM([日志保留]) AS [总日志保留] FROM fn_dblog(NULL,NULL); -- 约88.72MB /************************************* 场景2 **************************************/ -- 清除数据库并重新开始 使用[主控]; 去 IF DATABASEPROPERTYEX (N'SampleDB', N'版本') > 0 开始 更改数据库 [SampleDB] 设置单用户 立即回滚; 删除数据库[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 , 最大大小 = 100MB , 文件增长 = 4MB ); 去 使用[SampleDB]; 去 -- 添加一个表 创建表 dbo.test ( c1 CHAR(8000) NOT NULL 默认复制('a',8000) ) 在[主要]; 去 -- 确保我们不是伪简单的恢复模型 备份数据库 SampleDB 到磁盘='NUL'; 去 -- 备份日志文件 备份日志SampleDB 到磁盘='NUL'; 去 -- 现在,让我们炸毁事务中的日志文件 开始交易 插入 dbo.test 默认值; 去10000 -- 回滚永远不会触发。尝试一下。你会得到一个错误。 -- 消息 3903,第 16 级,状态 1,第 1 行 -- ROLLBACK TRANSACTION 请求没有对应的 BEGIN TRANSACTION。 回滚事务; -- 日志文件是否已 100% 满? DBCC SQLPERF(日志空间); -- fn_dblog() 有多少条记录可见? 选择 * FROM fn_dblog(NULL,NULL); -- 以我为例,大约为 91,926 去 -- fn_dblog() 显示的总日志保留? SELECT SUM([日志保留]) AS [总日志保留] FROM fn_dblog(NULL,NULL); -- 88.72MB 去