實例分析數據庫死鎖的產生

  近日由於系統操作過程中會提示“事務(進程 ID 54)與另一個進程被死鎖在 鎖 資源上,並且已被選作死鎖犧牲品。請重新運行該事務。”

  以前也出現過,但是無從下手,不知道該從哪裏下手。朱總提示應該以出錯這條語句訪問到的表爲中心查找所有跟此表有關的sql語句,看有沒有可能造成死鎖。其實聽到這個提示,我腦子裏也是懵的。後來一看被犧牲的這條sql語句有三個表,而且是使用最頻繁的三個表,如果按朱總的想法那工作量有多大啊?再加上我對數據庫死鎖的一些細節還有點不清楚,這不是大海撈針嘛?

  第二天我想了個辦法,首先重現這種死鎖,然後通過數據庫找到造成死鎖的前後sql,這樣範圍就小多了(說不準還能悟透死鎖的細節,當然這個是事前沒有想到的)。

  說做就做,第一步就是重現死鎖,正好很久以前爲測試這個系統的併發寫了一個機器人,能模仿多個客戶端做常規操作。但是有些日子沒更新了,修改了大概2個小時,終於跑起來了,還不錯,能用。18個機器人做常規操作,我自己在客戶端做非常規操作,客戶端一停頓下來,馬上運行exec sp_who_lock。

  經過手動整理如下:

54被86阻塞update SALE_Object set ObjStateID=@ObjStateID

71、76、86、101被54阻塞select * from SALE_ObjState, SALE_Object, SALE_SubSvc

60、66被71阻塞update SALE_Object set ObjStateID=@ObjStateID

 

  通過了解“獨佔鎖X” “共享鎖S”的含意總結如下:“”

-- select在ReadCommitted事務中加共享鎖,在RepeatableRead事務中加排它鎖;

-- INSERT、UPDATE、DELETE過程中始終應用排它鎖;

-- update必須等待select事務釋放共享鎖轉爲排它鎖後才能執行;

-- 若事務T對數據對象A加上S鎖,則其它事務只能再對A加S鎖,而不能加X鎖,直到T釋放A上的S鎖

 

  並查詢代碼中調用上面相關sql語句之前的語句,結論如下:
-- 86號進程ReadCommitted
86 insert SALE_ObjState 得到 SALE_ObjState 獨佔鎖
86 select SALE_ObjState 保持獨佔鎖
86 update 需要 SALE_Object 獨佔鎖

-- 54號進程RepeatableRead
54 select * from SALE_SubSvc, SALE_Object, SALE_ObjState
   依次得到SALE_SubSvc, SALE_Object的獨佔鎖,需要SALE_ObjState獨佔鎖但是被86佔了,得不到(這裏根據sql中的from語句依次得到表的鎖是我自己悟到的,因爲作爲程序不可能給三個表同時加鎖,必然是一個一個加的鎖,so……)

-- 71號進程與86號進程的過程是一樣的(此處省略)

 

由於54號進程中使用的是級別比較高級的事務,這也是造成死鎖最關鍵的一點。

解決辦法:

1.如果程序中54進程的查詢沒有必要使用RepeatableRead事務,那麼就將其修改爲ReadCommitted事務。

2.如果程序中必須使用RepeatableRead事務,那麼就必須通過調換sql語句select * from SALE_SubSvc, SALE_Object, SALE_ObjStateSALE_Object和SALE_ObjState的順序來達到目的。但是這個調換也是有風險的,因爲你不清楚是否還有其它位置和此處產生死鎖。這個解決方案比較複雜,不好從理論上進行細緻地討論。

  此文的目的是讓讀者理解數據庫死鎖產生的一些細節,如果有什麼描述得不清楚的地方請指出,我一定答覆。

  由於本人的水平是菜鳥級的,文中有理解得不對的地方歡迎指正!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章