在處理SSIS包的數據ETL操作過程中,我們經常遇到的一個問題就是一系列步驟在運行的過程中,如果中間的一個步驟失敗了,那么我們就需要清理前面已經運行過的步驟所產生的數據或者結果,這往往是一個很頭疼的過程。那么在SSIS的Package中是否可以實現事務機制呢?
我們知道基於事務我們可以保證在一系列操作下的各個步驟,它們要么全部成功,要么全部失敗。這里將介紹在SSIS的Package中一個比較簡單的實現方法。
首先,建立一個測試表,這個表里會有一個自增的主鍵標識,然后分別有一個文本和數字類型的字段。腳本如下:
USE [DBTEST]
CREATE TABLE [dbo].[TBTest](
[id] [int] IDENTITY(1,1) NOT NULL,
[Title] [nvarchar](50) NULL,
[Amount] [decimal](18, 0) NULL,
CONSTRAINT [PK_TBTest] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
在SSIS下新建立一個Package,然后加入如下三個步驟:
首先將數據表中的數據清空,然后INSERT幾條數據,最后嘗試進行一個失敗的更新。

在LOAD DATA步驟中,簡單的手動插入幾條數據。

數據源語句的查詢。

然后在第三步更新數據的步驟中,我們嘗試對主鍵進行更新,這里的目的主要就是要引發一個異常然后讓后續介紹的事務進行回滾。

首先看一下直接運行的結果。

可以看到在第三步中觸發的異常。

並且在表中可以看到,數據確實沒有被回滾,還在表中。

接下來,我們開始嘗試在這個Package中加入事務機制。

如上圖,SSIS的這種模塊化真的是非常好,相信大家一看這個圖就立刻明白接下來要做什么了。
在BEGINTRAN模塊中的代碼:
BEGIN TRANSACTION;
在COMMITTRAN模塊中的代碼:
COMMIT TRANSACTION;
最后,在ROLL BACK模塊中的代碼:
ROLLBACK TRANSACTION;
然后,運行包。

發現在ROLL BACK模塊中還是報錯了,錯誤信息如下:

並且,事務沒有回滾。在表中還是可以看到被INSERT的數據。

這里的關鍵在於,每一個模塊默認利用SSIS里的數據源連接,都是重新開啟一個新的連接,所以這樣在一個新連接里的ROLLBACK沒有前文,肯定是要失敗的。
所以,這里需要關注SSIS包數據源連接的一個屬性,就是RetainSameConnection,它默認為False,把它設置成True,就可以保證在一個包里調用的數據源連接都是同一個連接。

設置好這個屬性之后,我們再來運行下包。

可以發現,當數據流在有異常被觸發的時候,ROLL BACK模塊成功的進行了回滾。

從表中發現,數據確實被回滾了。
其實實現數據回滾的方法也很多,這是利用SSIS自帶功能的一個實現,他確實實現起來相對簡單一些。這樣可以避免包失敗后,重新運行包導致前面的步驟被重復運行。園子里另外一個兄弟BI Work介紹的這篇文章利用Check Point來避免這種情況的發生。除此之外,也可以在設計Package的時候,在包的開頭就設計好對可能影響到的數據的清理工作。總之實現的方法很多,在實際項目中完全可以根據實際的情況來決定使用哪一個方案。
另外,在SSIS中實際上也可以利用MSDTC,但是它實現起來多少有一定的門檻,如果你對MSDTC感興趣可以參考園子里另外一個朋友對它的介紹。
