一個典型的數(shù)據(jù)庫操作事務(wù)死鎖分析
【表A】與【表B】之間有外鍵約束(具體怎么約束的無所謂,因為外鍵和事務(wù)死鎖沒有絕對關(guān)系)。【表A】=主鍵表,【表B】=外鍵表。
公司有幾位程序員寫的代碼總是出現(xiàn)死鎖,現(xiàn)在將事務(wù)死鎖情況重現(xiàn).
{
try{
for(
)//一個循環(huán){
if(查詢【表A】有該【記錄】==false)//這個查詢沒有用當(dāng)前事務(wù)的數(shù)據(jù)庫連接,而是新開一個數(shù)據(jù)庫連接查詢數(shù)據(jù)庫
{
將【記錄】插入【表A】;
插入【表B】;
}
}
事務(wù).提交();
}catch{
事務(wù).回滾();
}
}
4月25日早9:00補充:以上代碼邏輯如果放在一個存儲過程里面,是沒有問題的。但是放在C#中,程序員稍不注意,就很可能出現(xiàn)問題。出現(xiàn)問題的關(guān)鍵地方在“查詢【表A】”。C#中,如果查詢【表A】和當(dāng)前的事務(wù)不是同一個數(shù)據(jù)庫連接,而是新開一個連接,就會死鎖(可以看留言中我的回復(fù))。所以:要么
1,查詢【表A】改為使用當(dāng)前事務(wù)的數(shù)據(jù)庫連接(推薦用這種方法)
2,要么按下面的進(jìn)行代碼改造,將事務(wù)移到循環(huán)體內(nèi)。
當(dāng)循環(huán)只有1次的時候,上面代碼運行沒有問題。
一旦循環(huán)大于1次的時候,死鎖立即出現(xiàn),運行SQL Server 2005的sp_who_lock,發(fā)現(xiàn)死鎖的地方正是“查詢【表A】”這塊。為什么呢?
因為:第1次循環(huán),插入【表A】后,事務(wù)將【表A】設(shè)置了獨占鎖,但是第1次循環(huán)完后,事務(wù)并沒有提交,也就沒有解開【表A】上的獨占鎖。因為表A被獨占鎖了,所以第2次循環(huán)時,“查詢【表A】”這個操作進(jìn)行不下去(后面的插入【表A】更是如此),一直在等待事務(wù)提交以解開鎖,但是事務(wù)運行到第2次循環(huán)的查詢【表A】就死了,循環(huán)無法繼續(xù)進(jìn)行,也就不能運行到循環(huán)外邊的事務(wù).提交(),【表A】的獨占鎖永遠(yuǎn)沒法解開。死鎖就這樣產(chǎn)生了。
既然知道了死鎖的原因是因為循環(huán)里面沒有解開獨占鎖,所以我們應(yīng)該把事務(wù).提交()放置在循環(huán)內(nèi)。另外根據(jù)事務(wù)體的邏輯盡量少的原則,我們把事務(wù)的聲明移植循環(huán)體內(nèi),使事務(wù)體的代碼行數(shù)盡量少。代碼如下。
)//一個循環(huán){
bool 有記錄=查詢【表A】有該【記錄】;//將這個查詢移出事務(wù)是良好的編碼習(xí)慣,雖然這里不必要
using(事務(wù))
{
try{
if(有記錄==false)
{
將【記錄】插入【表A】;
插入【表B】;
}
事務(wù).提交();
}catch{
事務(wù).回滾();
}
}
}
經(jīng)過以上改造之后,幾位程序員寫的業(yè)務(wù)運行正常。
由此總結(jié):
1,在C#等程序代碼中使用事務(wù),并在事務(wù)內(nèi)進(jìn)行查詢的時候,特別要小心,確保該查詢和事務(wù)使用的是同一個數(shù)據(jù)庫連接。防止表被獨占死鎖。
2,事務(wù)體內(nèi)應(yīng)盡可能少的邏輯,盡可能少的代碼行數(shù)。
3,4月25日9:00補充:防止事務(wù)死鎖最好的方法,還是大家推薦的用存儲過程,在存儲過程里面使用事務(wù)(只不過門檻稍高,手工活兒稍多)
本博客所有隨筆版權(quán)歸博客園和kai.ma所有,歡迎轉(zhuǎn)載,轉(zhuǎn)載請保留:
- 出處:http://kaima.cnblogs.com
- 作者:kai.ma

浙公網(wǎng)安備 33010602011771號