KB-Deadlock Detection And Retrial Sample
0 |
最近在處理表單引擎的Deadlock問題,由於引擎核心以物件導向方式開發,很難為了資料庫的更新問題去挪動物件的呼叫順序。但還是努力做了調整,每天Deadlock的發生次數終於壓到個位數,但要100%避免看來是不可能的(至少以我的能力來說是如此)。既然逃不掉,就乖乖面對吧!
一般處理Deadlock的準則是Wait And Retrial,換句話說,程式邏輯本身並沒有任何錯誤,純粹是運氣不好,跟其他Process的資料庫更新作業強碰且被SQL Server挑中變成犠牲品,在絕大部份的情況下,在我們接到Exception的同時,冤家Process也已完成它的作業,釋放Lock。所以只要再試一次,幾乎都會成功。
不過,由於Connection在歷經錯誤後,就無法再參與Tranascation,因此得重新建立Connection再跑。(我自己試的結果,連TransactionScope也要重來才會成功)
測試了好久,總算湊出Workable的演算法如下。其中DataProvider在建立時會開一條Connection,Dispose()時關閉,從頭到尾都用同一條連線做事,如此在TransactionScope可以用效率較佳的LTM機制,不必動用到OleTx(MSDTC)。重試的部分則利用SqlException.Number==-2(Lock timeout)或1205(Deadlock victim)來判定是否需要重試,若是則等待1秒後再試一次。若遇到的不是Lock或Deadlock問題,則沒有理由重試,直接throw Exception。
static void doUpdate()
{
using (TransactionScope tx = new TransactionScope())
{
using (DataProvider dp = new DataProvider())
{
//接連新增資料到Emp及Cust兩個Table
//如果同時有其他Process先新增Cust再新增Emp就會發生Deadlock
dp.ExecCommand(
"INSERT INTO Emp VALUES (999, 'Jeffrey', 1)");
dp.ExecCommand(
"INSERT INTO Cust VALUES (999, 'Darkthread', 1)");
}
tx.Complete();
}
}
static void testDeadlock()
{
for (int i = 0; i < 3; i++)
{
try
{
doUpdate();
break;
}
catch (SqlException se)
{
if (
se.Number == -2 //Lock Timeout
|| se.Number == 1205 //Deadlock
)
{
//理論上要寫Log記下這個錯誤
//這裡用Console.Write意思一下
Console.WriteLine(se.Message);
if (i == 2) //第三次不等了,丟Exception
throw new
ApplicationException("資料庫重試3次失敗!");
else
//前兩次,先等待一小段時間再跑迴圈重試
Thread.Sleep(1000);
}
else
throw;
}
}
}
Comments
Be the first to post a comment