Sql-Server

這兩個 SQL Server 回滾有何不同?

  • August 14, 2015

在 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 ServerLDF很快就開始使用文件。就我而言,它在最初的幾分鐘(< 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

場景二:

方案 2

如上所述,在執行了更多測試之後,我得出了一個經過計算的結論。我在這裡將所有這些內容匯總到一篇文中,但我會將一些內容複製到這篇博文中以供後人使用。

猜想(基於一些測試)

到目前為止,我還沒有明確的解釋為什麼會這樣。但以下是我基於測試期間收集的工件的估計。

回滾在這兩種情況下都會發生。一種是顯式回滾(使用者點擊取消按鈕),另一種是式回滾( Sql Server 在內部做出該決定)。

在這兩種情況下,流向日誌文件的流量是一致的。請參閱下面的圖片:

場景一:

場景一:

場景二:

方案 2

  • 強化這種構想的一個人工製品是在這兩種情況下擷取 Sql Trace。

    • 場景 1 是不言而喻的,也就是當我們點擊“取消”時,它會回滾。
    • 在場景 2 中,在隱式執行“回滾”後顯示錯誤消息。在 Sql Trace 中,我們看到錯誤消息“數據庫 ‘SampleDB’ 的事務日誌已滿”,該消息在螢幕上顯示很長時間之前。所以,我的猜測是回滾在這兩種情況下都會發生,但是錯誤消息是在成功並完全執行回滾後顯示場景 2。
  • 場景 2 似乎需要更長的時間,因為它進展得更遠,所以回滾需要更長的時間。

無法解釋的行為:

  • 為什麼日誌文件的使用變化如此之大?

    • 它增加到 90%,然後下降到 85%,然後上升到 99%,並在那裡徘徊了很長時間。我多次看到它像這樣上下波動:99.2%、99.8%、99.1%、99.7%。為什麼會這樣?
    • 一種可能的解釋是,可能有一個後台程序(類似於 Log Flush)每隔幾分鍾清理一次日誌文件。每次啟動時,都會清除一些條目,從而產生更多可用空間。

歡迎任何有助於以更好的方式解釋這種行為的想法。

我嘗試了以下實驗並得到了類似的結果。在這兩種情況下,fn_dblog() 都顯示發生了回滾,並且在場景 2 中似乎比在場景 1 中發生得更快。

順便說一句,我將 MDF 和 LDF 放在同一個外部 (USB 2.0) 磁碟上。

我最初的結論是,在這種情況下,回滾操作沒有區別,並且可能任何明顯的速度差異都與 I/O 子系統有關。這只是我目前的工作假設。

場景一:

  • 創建一個數據庫,其日誌文件從 1MB 開始,以 4MB 塊增長,最大大小為 100MB。
  • 打開顯式事務,執行 10 秒,然後在 SSMS 中手動取消
  • 查看 fn_dblog() 計數和日誌保留大小並查看 DBCC SQLPERF(LOGSPACE)

場景二:

  • 創建一個數據庫,其日誌文件從 1MB 開始,以 4MB 塊增長,最大大小為 100MB。
  • 打開一個顯式事務,執行它直到日誌已滿錯誤出現
  • 查看 fn_dblog() 計數和日誌保留大小並查看 DBCC SQLPERF(LOGSPACE)

性能監視器結果:

場景一: 場景 1

場景二: 場景 2

程式碼:

使用[主];
走

IF DATABASEPROPERTYEX (N'SampleDB', N'Version') &gt; 0
開始
ALTER DATABASE [SampleDB] SET SINGLE_USER
立即回滾;
刪除數據庫 [SampleDB];
結尾;
走

在 PRIMARY 上創建數據庫 [SampleDB]
(
NAME = N'SampleDB'
, 文件名 = N'E:\data\SampleDB.mdf'
, 大小 = 3MB
, 文件增長 = 1MB
)
登錄
(
NAME = N'SampleDB_log'
, 文件名 = N'E:\data\SampleDB_log.ldf'
, 大小 = 1MB
, 最大尺寸 = 100MB
, 文件增長 = 4MB
);
走

使用[SampleDB];
走

-- 添加表格
創建表 dbo.test
(
c1 CHAR(8000) NOT NULL DEFAULT REPLICATE('a',8000)
) 在 [主要] 上;
走

-- 確保我們不是偽簡單的恢復模型
備份數據庫 SampleDB
到磁碟 = '零';
走

-- 備份日誌文件
備份日誌範例數據庫
到磁碟 = '零';
走

-- 檢查已使用的日誌空間
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([Log Reserve]) AS [Total Log Reserve]
FROM fn_dblog(NULL,NULL); -- 大約 88.72MB


/**********************************
情景 2
**********************************/
-- 吹走數據庫並重新開始
使用[主];
走

IF DATABASEPROPERTYEX (N'SampleDB', N'Version') &gt; 0
開始
ALTER DATABASE [SampleDB] SET SINGLE_USER
立即回滾;
刪除數據庫 [SampleDB];
結尾;
走

在 PRIMARY 上創建數據庫 [SampleDB]
(
NAME = N'SampleDB'
, 文件名 = N'E:\data\SampleDB.mdf'
, 大小 = 3MB
, 文件增長 = 1MB
)
登錄
(
NAME = N'SampleDB_log'
, 文件名 = N'E:\data\SampleDB_log.ldf'
, 大小 = 1MB
, 最大尺寸 = 100MB
, 文件增長 = 4MB
);
走

使用[SampleDB];
走

-- 添加表格
創建表 dbo.test
(
c1 CHAR(8000) NOT NULL DEFAULT REPLICATE('a',8000)
) 在 [主要] 上;
走

-- 確保我們不是偽簡單的恢復模型
備份數據庫 SampleDB
到磁碟 = '零';
走

-- 備份日誌文件
備份日誌範例數據庫
到磁碟 = '零';
走

-- 現在,讓我們炸毀事務中的日誌文件
開始交易
插入 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([Log Reserve]) AS [Total Log Reserve]
FROM fn_dblog(NULL,NULL); -- 88.72MB
走

引用自:https://dba.stackexchange.com/questions/110600