程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> 其他數據庫知識 >> 更多數據庫知識 >> SQL Server誤區30日談 第26天 SQL Server中存在真正的“事務嵌套”

SQL Server誤區30日談 第26天 SQL Server中存在真正的“事務嵌套”

編輯:更多數據庫知識

誤區 #26: SQL Server中存在真正的“事務嵌套”
錯誤

    嵌套事務可不會像其語法表現的那樣看起來允許事務嵌套。我真不知道為什麼有人會這樣寫代碼,我唯一能夠想到的就是某個哥們對SQL Server社區嗤之以鼻然後寫了這樣的代碼說:“玩玩你們”。
    讓我更詳細的解釋一下,SQL Server允許你在一個事務中開啟嵌套另一個事務,SQL Server允許你提交這個嵌套事務,也允許你回滾這個事務。
    但是,嵌套事務並不是真正的“嵌套”,對於嵌套事務來說SQL Server僅僅能夠識別外層的事務。嵌套事務是日志不正常增長的罪魁禍首之一因為開發人員以為回滾了內層事務,僅僅是回滾內層事務。
    但實際上當回滾內層事務時,會回滾整個事務,而不是僅僅是內層。這也是為什麼我說嵌套事務並不存在。
    所以作為開發人員來講,永遠不要對事務進行嵌套。事務嵌套是邪惡的。
    如果你不相信我說的,那麼通過下面的例子就就會相信。創建完數據庫和表之後,每一條記錄都會導致日志增加8K。

復制代碼 代碼如下:
CREATE DATABASE NestedXactsAreNotReal;
GO
USE NestedXactsAreNotReal;
GO
ALTER DATABASE NestedXactsAreNotReal SET RECOVERY SIMPLE;
GO
CREATE TABLE t1 (c1 INT IDENTITY, c2 CHAR (8000) DEFAULT 'a');
CREATE CLUSTERED INDEX t1c1 ON t1 (c1);
GO
SET NOCOUNT ON;
GO

測試 #1:回滾內部事務時僅僅回滾內部事務?
復制代碼 代碼如下:
BEGIN TRAN OuterTran;
GO
INSERT INTO t1 DEFAULT Values;
GO 1000
BEGIN TRAN InnerTran;
GO
INSERT INTO t1 DEFAULT Values;
GO 1000
SELECT @@TRANCOUNT, COUNT (*) FROM t1;
GO

你可以看到得出的結果是2和2000,下面我來回滾內部的事務,按照我們的猜想應該只回滾1000條吧,但事實上你會得到如下結果:
復制代碼 代碼如下:
ROLLBACK TRAN InnerTran;
GO

復制代碼 代碼如下:
消息 6401,級別 16,狀態 1,第 2 行
無法回滾 InnerTran。找不到該名稱的事務或保存點。

好吧,由Books Online來看,我只能使用外部事務的名稱或是將事務名稱留空來進行回滾,代碼如下:
復制代碼 代碼如下:
ROLLBACK TRAN;
GO
SELECT @@TRANCOUNT, COUNT (*) FROM t1;
GO

現在我得到結果是0和0。正如Books Online所言,這個回滾操作將外部事務進行了回滾並將全局變量@@TRANCOUNT設置為0。事務中所有的修改都被回滾,如果想部分回滾的話只能使用SAVE TRAN 和ROLLBACK TRAN。
測試 #2:嵌套事務中內部事務提交後會保存內部事務的修改嗎?
復制代碼 代碼如下:
BEGIN TRAN OuterTran;
GO
BEGIN TRAN InnerTran;
GO
INSERT INTO t1 DEFAULT Values;
GO 1000
COMMIT TRAN InnerTran;
GO
SELECT COUNT (*) FROM t1;
GO

正如我所期待,得到的結果是1000。這說明內部事務提交是會修改到磁盤的。但是如果這時外部事務回滾的話,那麼不應該回滾內部事務…
復制代碼 代碼如下:
ROLLBACK TRAN OuterTran;
GO
SELECT COUNT (*) FROM t1;
GO

但運行上面查詢後結果是0,這說明外部事務的回滾會影響內部事務。

測試 #3:提交嵌套的事務的內部事務至少可以讓我清除日志吧。
在開始這個測試之前我首先清除了日志,然後運行如下代碼:
復制代碼 代碼如下:
BEGIN TRAN OuterTran;
GO
BEGIN TRAN InnerTran;
GO
INSERT INTO t1 DEFAULT Values;
GO 1000
DBCC SQLPERF ('LOGSPACE');
GO

得到結果:

下面我將事務提交後運行CheckPoint(對於簡單恢復模式的數據庫將會截斷日志),得到的結果:
復制代碼 代碼如下:
COMMIT TRAN InnerTran;
GO
CHECKPOINT;
GO
DBCC SQLPERF ('LOGSPACE');
GO



我們發現日志的使用不減反贈,這是由於日志寫入了CheckPoint記錄(詳情請看:How do checkpoints work and what gets logged)。提交內部事務不會導致日志被清除,這是由於外部事務回滾時也會連同內部事務一起回滾(譯者注:所以這部分VLF在外部事務提交之前永遠不會被標記位reusable)。所以這部分日志在外部事務提交之前永遠不會被截斷。為了證明這一點,我提交外部事務,然後再來看日志:
復制代碼 代碼如下:
COMMIT TRAN OuterTran;
GO
CHECKPOINT;
GO
DBCC SQLPERF ('LOGSPACE');
GO


麼樣,日志使用百分比大幅下降了吧。
對於嵌套事務來說---Just Say no。(這句話你可以當作來自SQLSkill.com的一個熱心的家伙給的福利:-)

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved