摘要:[筆記]TranscationScope造成的Table Lock現象
ASP.NET 2.0以來有一個機制方便我們包裝Transaction=>TransactionScope 類別
黑暗大很久以前也有介紹過=>KB-.NET 2.0 分散式交易新利器---TransactionScope
平常沒用到,等到用到在查看才發現到一些現象=>這些現象黑暗大就有提到了,我只是精簡幫自己做一個筆記。
精簡後的程式碼=>
TransactionOptions options = new TransactionOptions();
options.IsolationLevel = System.Transactions.IsolationLevel.RepeatableRead;
options.Timeout = new TimeSpan(0, 2, 0);
TransactionOptions options = new TransactionOptions();
options.IsolationLevel = System.Transactions.IsolationLevel.RepeatableRead;
options.Timeout = new TimeSpan(0, 2, 0);
using (System.Transactions.TransactionScope scope = new System.Transactions.TransactionScope(TransactionScopeOption.Required, options))
{
using (SqlConnection conn1 = new SqlConnection("Server=xxx;Database=xxx;uid=sa;pwd=xxx;"))
{
try
{
conn1.Open();
string cmdText = @"INSERT INTO T1 (C1,C2) VALUES ('1','1000')";
SqlCommand cmd1 = new SqlCommand(cmdText, conn1);
cmd1.ExecuteNonQuery();
string cmdText2 = @"UPDATE T2 SET C2 = '123456' FROM T2 WHERE C1 = '3'";
SqlCommand cmd2 = new SqlCommand(cmdText2, conn1);
cmd2.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
}
options.IsolationLevel = System.Transactions.IsolationLevel.RepeatableRead;
options.Timeout = new TimeSpan(0, 2, 0);
TransactionOptions options = new TransactionOptions();
options.IsolationLevel = System.Transactions.IsolationLevel.RepeatableRead;
options.Timeout = new TimeSpan(0, 2, 0);
using (System.Transactions.TransactionScope scope = new System.Transactions.TransactionScope(TransactionScopeOption.Required, options))
{
using (SqlConnection conn1 = new SqlConnection("Server=xxx;Database=xxx;uid=sa;pwd=xxx;"))
{
try
{
conn1.Open();
string cmdText = @"INSERT INTO T1 (C1,C2) VALUES ('1','1000')";
SqlCommand cmd1 = new SqlCommand(cmdText, conn1);
cmd1.ExecuteNonQuery();
string cmdText2 = @"UPDATE T2 SET C2 = '123456' FROM T2 WHERE C1 = '3'";
SqlCommand cmd2 = new SqlCommand(cmdText2, conn1);
cmd2.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
}
SELECT * FROM T1 SELECT * FROM T2
當我們開始執行時,可以中斷在ExecuteNonQuery
斷在第一段Insert會發現T1被Lock了,T2沒被Lock
再往下跑到Update的ExecuteNonQuery
會發現T1,T2都被Lock了
這是用
SELECT * FROM T1 SELECT * FROM T2
的結果。如同黑暗大說的,是以Table Lock的方式。
然而,以前會被前輩教導,select table都要加上with nolock
因此
SELECT * FROM T1 WITH (NOLOCK) SELECT * FROM T2 WITH (NOLOCK)
但是萬一還沒commit前你就這樣抓到資料了,
程式有問題rollback了,這兩筆資料就不對了,
雖然說這時間很短,我沒中斷的話也感覺不出有Table Lock,
但是交易系統幾萬筆資料在跑,你難保不會那一天出這個問題,
到時要追就追不到。
所以需要的是抓到已經commit的資料來處理
這時就可以用
SELECT * FROM T1 WITH (READPAST) SELECT * FROM T2 WITH (READPAST)
接下來,我們來看Table Lock的範圍,其實他並不是整個Table都不能存取的,
string cmdText = @"INSERT INTO T1 (C1,C2) VALUES ('2','2000')";
SqlCommand cmd1 = new SqlCommand(cmdText, conn1);
cmd1.ExecuteNonQuery();
string cmdText2 = @"UPDATE T2 SET C2 = '123456' FROM T2 WHERE C1 = '1'";
SqlCommand cmd2 = new SqlCommand(cmdText2, conn1);
cmd2.ExecuteNonQuery();
SqlCommand cmd1 = new SqlCommand(cmdText, conn1);
cmd1.ExecuteNonQuery();
string cmdText2 = @"UPDATE T2 SET C2 = '123456' FROM T2 WHERE C1 = '1'";
SqlCommand cmd2 = new SqlCommand(cmdText2, conn1);
cmd2.ExecuteNonQuery();
一樣中斷在第二段指令
SELECT * FROM T1 SELECT * FROM T2
依然是Lock的狀態
select * from t1 where c1 = '1' select * from t2 where c1 = '2'
這段Query依然也是Lock的狀態,
此時我把T1,T2的C1攔位都加上PK,
再來下這段Query
我們就可以針對該筆去存取了
所以我們可以看到有PK的Table就可以做到是Record Lock
但是萬一你下的where條件沒有在PK,或是包含Lock那一筆資料,還是會Lock的。