因為同事把 READ_COMMITTED_SNAPSHOT 和 ALLOW_SNAPSHOT_ISOLATION 與 NOLOCK 這三個搞混,想說既然要說明就整理下來,順便當作紀錄
換到新的工作,接觸 SQL Server 的機會反而變多了,也相對要去摸一些 Oracle 或其他的關聯式資料庫,剛好在今天會議中有同事提到「快照隔離」,因此就順手整理一下相關的設定。
為了要做這個範例,我先建立一個範例資料庫,也放了一些測試資料
CREATE TABLE T1
( F1 INT PRIMARY KEY,
F2 NVARCHAR(10)
)
GO
CREATE TABLE T2
( F1 INT PRIMARY KEY,
F2 NVARCHAR(10)
)
GO
INSERT INTO T1 VALUES (1, 'A'),(2,'B'),(3,'C')
INSERT INTO T2 VALUES (1, 'A'),(2,'B'),(3,'C')
目前 SQL Server 預設的交易層級是 READ COMMITED,也因此當我們有個交易類似下面的狀況
那當目前有資料有被鎖定的時候,則如果有其他的 session 要去連接,那麼就會被卡住。我們可以透過活動監視器查看,可以看到 SESSION ID 52 後的被 SESSION ID 57 後給封鎖,因此就會無法讀取資料。
以前在處理類似狀況的時候,會看到有人會提出那麼採用 NOLOCK 的方式,就沒有問題了。但我們實際測試一下,雖然 SELECT 是可以執行,但所看到的是前面交易寫入一半尚未確認的資料,因此這個可能會是個比較有疑慮的資料。為什麼說是有疑慮,因為前面的交易可能會放棄,那麼可能資料就不會是 X ,而是原本的欄位值 A,但那個值又不能算錯,因為是您要取得還在記憶體內的 Dirty Data。
基本上這個的用法還要注意,那麼 NOLOCK 是針對所要下的資料表去設定,因此如果您有關聯多個資料表,而每個資料表又想忽略掉鎖定,那麼就可以要寫成類似以下的寫法
其實早在 SQL Server 2008 的時候,就已經有提供 ALLOW READ COMMITTED SNAPSHOT 和 ALLOW SNAPSHOT ISOLATION 這兩種選項,只是有些時候有朋友會被這兩個給搞混,因此下面我就各自做一個範例來做說明。
首先我先測試「啟用讀取認可快照」,這個設定是按照資料庫來做設定,因此在測試的資料庫我將這個屬性給設定為 True
那接下來我按照前面的範例,依然去鎖定 T1 資料表的一筆資料,此時可以看到在讀取資料的時候,是不會因為資料被鎖定,而無法讀取的狀況,而是看到更新前的舊欄位值。因此如果要做這樣的設定,基本上就屬於資料庫做相關設定調整,應該程式則不需要做相關的修改。
但如果這個是後我設定的是「允許快照集隔離」
那麼在這樣的情況下,您如果類似我的範例去修改資料,則會跟一開始都沒有設定的一樣,因為預設資料庫交易層級是 READ COMMIT ,所以其他的 session 的 SELECT 同一個資料表,就會發生鎖定的狀況。因此如果我希望交易能有快照及隔離,那麼就要改成類似以下的作法,在交易前增加設定交易層級為快照及隔離
那這樣針對這個交易,SQL Server 就會允許其他 session ,在讀取這個資料表的時候是可以讀取到舊值。因此您就可以按照您自己的需要,針對有需要交易去做設定,其他沒有設定的就按照原本 READ COMMIT 的交易層級。