MySQL產生死鎖的根本原因及解決方法
一、 什麼是死鎖
死鎖是指兩個或兩個以上的程序在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去.此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等的程序稱為死鎖程序.
二、 死鎖產生的四個必要條件
•互斥條件:指程序對所分配到的資源進行排它性使用,即在一段時間內某資源只由一個程序佔用。如果此時還有其它程序請求資源,則請求者只能等待,直至佔有資源的程序用畢釋放
•請求和保持條件:指程序已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它程序佔有,此時請求程序阻塞,但又對自己已獲得的其它資源保持不放
•不剝奪條件:指程序已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放
•環路等待條件:指在發生死鎖時,必然存在一個程序——資源的環形鏈,即程序集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1佔用的資源;P1正在等待P2佔用的資源,……,Pn正在等待已被P0佔用的資源
這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之一不滿足,就不會發生死鎖。
三、 如何處理死鎖
1) 鎖模式
1.共享鎖(S)
由讀操作建立的鎖,防止在讀取資料的過程中,其它事務對資料進行更新;其它事務可以併發讀取資料。共享鎖可以加在表、頁、索引鍵或者資料行上。在SQL SERVER預設隔離級別下資料讀取完畢後就會釋放共享鎖,但可以通過鎖提示或設定更高的事務隔離級別改變共享鎖的釋放時間。
2.獨佔鎖(X)
對資源獨佔的鎖,一個程序獨佔地鎖定了請求的資料來源,那麼別的程序無法在此資料來源上獲得任何型別的鎖。獨佔鎖一致持有到事務結束。
3.更新鎖(U)
更新鎖實際上並不是一種獨立的鎖,而是共享鎖與獨佔鎖的混合。當SQL SERVER執行資料修改操作卻首先需要搜尋表以找到需要修改的資源時,會獲得更新鎖。
更新鎖與共享鎖相容,但只有一個程序可以獲取當前資料來源上的更新鎖,
其它程序無法獲取該資源的更新鎖或獨佔鎖,更新鎖的作用就好像一個序列化閥門(serialization gate),將後續申請獨佔鎖的請求壓入佇列中。持有更新鎖的程序能夠將其轉換成該資源上的獨佔鎖。更新鎖不足以用於更新資料—實際的資料修改仍需要用到獨佔鎖。對於獨佔鎖的序列化訪問可以避免轉換死鎖的發生,更新鎖會保留到事務結束或者當它們轉換成獨佔鎖時為止。
4. 意向鎖(IX,IU,IS)
意向鎖並不是獨立的鎖定模式,而是一種指出哪些資源已經被鎖定的機制。
如果一個表頁上存在獨佔鎖,那麼另一個程序就無法獲得該表上的共享表鎖,這種層次關係是用意向鎖來實現的。程序要獲得獨佔頁鎖、更新頁鎖或意向獨佔頁鎖,首先必須獲得該表上的意向獨佔鎖。同理,程序要獲得共享行鎖,必須首先獲得該表的意向共享鎖,以防止別的程序獲得獨佔表鎖。
5. 特殊鎖模式(Sch_s,Sch_m,BU)
SQL SERVER提供3種額外的鎖模式:架構穩定鎖、架構修改鎖、大容量更新鎖。
6.轉換鎖(SIX,SIU,UIX)
轉換鎖不會由SQL SERVER 直接請求,而是從一種模式轉換到另一種模式所造成的。SQL SERVER 2008支援3種類型的轉換鎖:SIX、SIU、UIX.其中最常見的是SIX鎖,如果事務持有一個資源上的共享鎖(S),然後又需要一個IX鎖,此時就會出現SIX。
7.鍵範圍鎖
鍵範圍鎖是在可序列化隔離級別中鎖定一定範圍內資料的鎖。保證在查詢資料的鍵範圍內不允許插入資料。
SQL SERVER 鎖模式 |
||
縮寫 |
鎖模式 |
說明 |
S |
Shared |
允許其他程序讀取但不能修改鎖定的資源 |
X |
Exclusive |
防止別的程序讀取或者修改鎖定資源中的資料 |
U |
Update |
防止其它程序獲取更新鎖或獨佔鎖;在搜尋要修改的資料時使用 |
IS |
Intent shared |
表示該資源的一個元件被共享鎖鎖定了。只有在表或頁級別才能獲得這類鎖 |
IU |
Intent update |
表示該資源的一個元件被更新鎖鎖定了。只有在表或頁級別才能獲得這類鎖 |
IX |
Intent exclusive |
表示該資源的一個元件被獨佔鎖鎖定了。只有在表或頁級別才能獲得這類鎖 |
SIX |
Shared with intent exclusive |
表示一個正持有共享鎖的資源還有一個元件(一頁或一行)被獨佔鎖鎖定了 |
SIU |
Shared with intent Update |
表示一個正持有共享鎖的資源還有一個元件(一頁或一行)被更新鎖鎖定了 |
UIX |
Update with intent exclusive |
表示一個正持有更新鎖的資源還有一個元件(一頁或一行)被獨佔鎖鎖定了 |
Sch-S |
Schema stability |
表示一個使用該表的查詢正在被編譯 |
Sch-M |
Schema modification |
表示表的結構正在被修改 |
BU |
Bulk Update |
在一個大容量複製操作將資料匯入表中並且(手動或自動)應用了TABLOCK查 詢提示時使用 |
2) 鎖粒度
SQL SERVER 可以在表、頁、行等級別鎖定使用者的資料資源即非系統資源(系統資源是用閂鎖來保護的)。此外SQL SERVER 還可以鎖定索引鍵和索引鍵範圍。
通過sys.dm_tran_locks檢視可以檢視誰被鎖定了(如行,鍵,頁)、鎖的模式以及特定資源的標誌符。基於sys.dm_tran_locks檢視建立如下檢視用於檢視鎖定的資源以及鎖模式(通過這個檢視可以檢視事務鎖定的表、頁、行以及加在資料資源上的鎖型別)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
CREATE VIEW
dblocks AS
SELECT request_session_id
AS spid,
DB_NAME(resource_database_id) AS
dbname,
CASE WHEN
resource_type= 'object'
THEN OBJECT_NAME(resource_associated_entity_id)
WHEN resource_associated_entity_id=0
THEN 'n/a'
ELSE OBJECT_NAME(p.object_id)
END AS
entity_name,
index_id,
resource_type AS
RESOURCE,
resource_description AS
DESCRIPTION,
request_mode AS
mode,
request_status AS
STATUS
FROM sys.dm_tran_locks t
LEFT JOIN
sys.partitions p ON
p.partition_id=t.resource_associated_entity_id
WHERE resource_database_id=DB_ID()
|
3) 如何跟蹤死鎖
通過選擇sql server profiler 事件中的如下選項就可以跟蹤到死鎖產生的相關語句。
4) 死鎖案例分析
在該案例中process65db88, process1d0045948為語句1的程序,process629dc8 為語句2的程序; 語句2獲取了1689766頁上的更新鎖,在等待1686247頁上的更新鎖;而語句1則獲取了1686247頁上的更新鎖在等待1689766頁上的更新鎖,兩個語句等待的資源形成了一個環路,造成死鎖。
5) 如何解決死鎖
針對如上死鎖案例,分析其對應語句執行計劃如下:
通過執行計劃可以看出,在查詢需要更新的資料時使用的是索引掃描,比較耗費效能,這樣就造成鎖定資源時間過長,增加了語句併發執行時產生死鎖的概率。
處理方式:
1. 在表上建立一個聚集索引。
2. 對語句更新的相關欄位建立包含索引。
優化後該語句執行計劃如下:
優化後的執行計劃使用了索引查詢,將大幅提升該查詢語句的效能,降低了鎖定資源的時間,同時也減少了鎖定資源的範圍,這樣就降低了鎖資源迴圈等待事件發生的概率,對於預防死鎖的發生會有一定的作用。
死鎖是無法完全避免的,但如果應用程式適當處理死鎖,對涉及的任何使用者及系統其餘部分的影響可降至最低(適當處理是指發生錯誤1205時,應用程式重新提交批處理,第二次嘗試大多能成功。一個程序被殺死,它的事務被取消,它的鎖被釋放,死鎖中涉及到的另一個程序就可以完成它的工作並釋放鎖,所以就不具備產生另一個死鎖的條件了。)
四、 如何預防死鎖
阻止死鎖的途徑就是避免滿足死鎖條件的情況發生,為此我們在開發的過程中需要遵循如下原則:
1.儘量避免併發的執行涉及到修改資料的語句。
2.要求每一個事務一次就將所有要使用到的資料全部加鎖,否則就不允許執行。
3.預先規定一個加鎖順序,所有的事務都必須按照這個順序對資料執行封鎖。如不同的過程在事務內部對物件的更新執行順序應儘量保證一致。
4.每個事務的執行時間不可太長,對程式段的事務可考慮將其分割為幾個事務。在事務中不要求輸入,應該在事務之前得到輸入,然後快速執行事務。
5.使用盡可能低的隔離級別。
6.資料儲存空間離散法。該方法是指採用各種手段,將邏輯上在一個表中的資料分散的若干離散的空間上去,以便改善對錶的訪問效能。主要通過將大表按行或者列分解為若干小表,或者按照不同的使用者群兩種方法實現。
7.編寫應用程式,讓程序持有鎖的時間儘可能短,這樣其它程序就不必花太長的時間等待鎖被釋放。
補充:
死鎖的概念:
如果一組程序中的每一個程序都在等待僅由該組程序中的其他程序才能引發的事件,那麼改組程序是死鎖的。
死鎖的常見表現:
死鎖不僅會發生多個程序中,也會發生在一個程序中。
(1)多程序死鎖:有程序A,程序B,程序A擁有資源1,需要請求正在被程序B佔有的資源2。而程序B擁有資源2,請求正在被程序A戰友的資源1。兩個程序都在等待對方釋放資源後請求該資源,而相互僵持,陷入死鎖。
(2)單程序死鎖:程序A擁有資源1,而它又在請求資源1,而它所請求的資源1必須等待該資源使用完畢得到釋放後才可被請求。這樣,就陷入了自己的死鎖。
產生死鎖的原因:
(1)程序推進順序不當造成死鎖。
(2)競爭不可搶佔性資源引起死鎖。
(3)競爭可消耗性資源引起死鎖。
死鎖的四個必要條件(四個條件四者不可缺一):
(1)互斥條件。某段時間內,一個資源一次只能被一個程序訪問。
(2)請求和保持條件。程序A已經擁有至少一個資源,此時又去申請其他資源,而該資源又正在被程序使用,此時請求程序阻塞,但對自己已經獲得的資源保持不放。
(3)不可搶佔資源。程序已獲得的資源在未使用完不能被搶佔,只能在自己使用完時由自己釋放。
(4)迴圈等待序列。存在一個迴圈等待序列P0P1P2……Pn,P0請求正在被程序P1佔有的資源,P1請求正在被P2佔有的資源……Pn正在請求被程序P0佔有的資源。
解除死鎖的兩種方法:
(1)終止(或撤銷)程序。終止(或撤銷)系統中的一個或多個死鎖程序,直至打破迴圈環路,使系統從死鎖狀態中解除出來。
(2)搶佔資源。從一個或多個程序中搶佔足夠數量的資源,分配給死鎖程序,以打破死鎖狀態。