公司之前一直存在一個(gè)規范,就是禁止嵌套事務(wù)的使用,一直不太明白為什么,試了下應該是無(wú)法控制回滾,今天看大牛的博客發(fā)現,問(wèn)題遠遠不只如此。
具體總結下來(lái)是以下3個(gè)問(wèn)題
1、內層事務(wù)回滾,只能回滾全部事務(wù),無(wú)法控制單一事務(wù)回滾
2、內層事務(wù)提交后,回滾外層事務(wù),也會(huì )把內層提交了的事務(wù)一起回滾
3、因為2的原因,只要整個(gè)事務(wù)不完全提交,日志空間都無(wú)法被釋放
嵌套事務(wù)可不會(huì )像其語(yǔ)法表現的那樣看起來(lái)允許事務(wù)嵌套。我真不知道為什么有人會(huì )這樣寫(xiě)代碼,我唯一能夠想到的就是某個(gè)哥們對SQL Server社區嗤之以鼻然后寫(xiě)了這樣的代碼說(shuō):“玩玩你們”。
讓我更詳細的解釋一下,SQL Server允許你在一個(gè)事務(wù)中開(kāi)啟嵌套另一個(gè)事務(wù),SQL Server允許你提交這個(gè)嵌套事務(wù),也允許你回滾這個(gè)事務(wù)。
但是,嵌套事務(wù)并不是真正的“嵌套”,對于嵌套事務(wù)來(lái)說(shuō)SQL Server僅僅能夠識別外層的事務(wù)。嵌套事務(wù)是日志不正常增長(cháng)的罪魁禍首之一因為開(kāi)發(fā)人員以為回滾了內層事務(wù),僅僅是回滾內層事務(wù)。
但實(shí)際上當回滾內層事務(wù)時(shí),會(huì )回滾整個(gè)內層事務(wù),而不是僅僅是內層。這也是為什么我說(shuō)嵌套事務(wù)并不存在。
所以作為開(kāi)發(fā)人員來(lái)講,永遠不要對事務(wù)進(jìn)行嵌套。事務(wù)嵌套是邪惡的。
如果你不相信我說(shuō)的,那么通過(guò)下面的例子就就會(huì )相信。創(chuàng )建完數據庫和表之后,每一條記錄都會(huì )導致日志增加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:回滾內部事務(wù)時(shí)僅僅回滾內部事務(wù)?
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,下面我來(lái)回滾內部的事務(wù),按照我們的猜想應該只回滾1000條吧,但事實(shí)上你會(huì )得到如下結果:
ROLLBACK TRAN InnerTran;
GO
消息 6401,級別 16,狀態(tài) 1,第 2 行
無(wú)法回滾 InnerTran。找不到該名稱(chēng)的事務(wù)或保存點(diǎn)。
好吧,由Books Online來(lái)看,我只能使用外部事務(wù)的名稱(chēng)或是將事務(wù)名稱(chēng)留空來(lái)進(jìn)行回滾,代碼如下:
ROLLBACK TRAN;
GO
SELECT @@TRANCOUNT, COUNT (*) FROM t1;
GO
現在我得到結果是0和0。正如Books Online所言,這個(gè)回滾操作將外部事務(wù)進(jìn)行了回滾并將全局變量@@TRANCOUNT設置為0。事務(wù)中所有的修改都被回滾,如果想部分回滾的話(huà)只能使用SAVE TRAN 和ROLLBACK TRAN。
測試 #2:嵌套事務(wù)中內部事務(wù)提交后會(huì )保存內部事務(wù)的修改嗎?
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。這說(shuō)明內部事務(wù)提交是會(huì )修改到磁盤(pán)的。但是如果這時(shí)外部事務(wù)回滾的話(huà),那么不應該回滾內部事務(wù)…
ROLLBACK TRAN OuterTran;
GO
SELECT COUNT (*) FROM t1;
GO
但運行上面查詢(xún)后結果是0,這說(shuō)明外部事務(wù)的回滾會(huì )影響內部事務(wù)。
測試 #3:提交嵌套的事務(wù)的內部事務(wù)至少可以讓我清除日志吧。
在開(kāi)始這個(gè)測試之前我首先清除了日志,然后運行如下代碼:
BEGIN TRAN OuterTran;
GO
BEGIN TRAN InnerTran;
GO
INSERT INTO t1 DEFAULT Values;
GO 1000
DBCC SQLPERF ('LOGSPACE');
GO
得到結果:
下面我將事務(wù)提交后運行CheckPoint(對于簡(jiǎn)單恢復模式的數據庫將會(huì )截斷日志),得到的結果:
COMMIT TRAN InnerTran;
GO
CHECKPOINT;
GO
DBCC SQLPERF ('LOGSPACE');
GO
我們發(fā)現日志的使用不減反贈,這是由于日志寫(xiě)入了CheckPoint記錄(詳情請看:How do checkpoints work and what gets logged)。提交內部事務(wù)不會(huì )導致日志被清除,這是由于外部事務(wù)回滾時(shí)也會(huì )連同內部事務(wù)一起回滾(譯者注:所以這部分VLF在外部事務(wù)提交之前永遠不會(huì )被標記位reusable)。所以這部分日志在外部事務(wù)提交之前永遠不會(huì )被截斷。為了證明這一點(diǎn),我提交外部事務(wù),然后再來(lái)看日志:
COMMIT TRAN OuterTran;
GO
CHECKPOINT;
GO
DBCC SQLPERF ('LOGSPACE');
GO
怎么樣,日志使用百分比大幅下降了吧。
對于嵌套事務(wù)來(lái)說(shuō)---Just Say no。(這句話(huà)你可以當作來(lái)自SQLSkill.com的一個(gè)熱心的家伙給的福利

轉載地址: http://www.cnblogs.com/CareySon/archive/2013/01/22/2871204.html
聯(lián)系客服