這篇文章主要講解了“死鎖是怎么產生的”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“死鎖是怎么產生的”吧!
Part1 問題
由于innodb engine status會記錄最近一次死鎖的細節信息,因此案例現場的信息是可以完整拿到的。用戶針對這個死鎖的問題,提出了疑問:數據更新的并不是同一行,使用的也是不同的索引,為什么會發生死鎖?(以下細節信息均已脫敏)
死鎖的兩個語句如下:
UPDATE tbl_deadlock SET col1 = 1, col2 = 1, update_time = 1603685523 WHERE (id1 = 6247476) AND (id2 = 74354) UPDATE tbl_deadlock SET col1 = 1, col2 = 1, update_time = 1603685523 WHERE (id1 = 6249219) AND (id2 = 74354)
精簡之后的 MySQL 死鎖信息如下:
===================================== 2020-10-26 12:14:30 7fd2642f5700 INNODB MONITOR OUTPUT ===================================== ...省略... ------------------------ LATEST DETECTED DEADLOCK ------------------------ 2020-10-26 12:12:03 7fd2846ed700 *** (1) TRANSACTION: TRANSACTION 1795660514, ACTIVE 0 sec starting index read mysql tables in use 3, locked 3 LOCK WAIT 4 lock struct(s), heap size 1184, 3 row lock(s) MySQL thread id 21829887, OS thread handle 0x7fd28d14a700, query id 178279444 172.21.0.15 username updating UPDATE tbl_deadlock SET col1= 1, col2 = 1, update_time = 1603685523 WHERE (id1 = 6247476) AND (id2 = 74354) *** (1) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 8575 page no 286947 n bits 1048 index `id2` of table `deadlock`.`tbl_deadlock` trx id 1795660514 lock_mode X waiting Record lock, heap no 429 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 00012272; asc "r;; 1: len 4; hex 00721f45; asc r E;; *** (2) TRANSACTION: TRANSACTION 1795660513, ACTIVE 0 sec fetching rows mysql tables in use 3, locked 3 20 lock struct(s), heap size 2936, 40 row lock(s) MySQL thread id 21905203, OS thread handle 0x7fd2846ed700, query id 178279443 172.21.0.15 username updating UPDATE tbl_deadlock SET col1 = 1, col2 = 1, update_time = 1603685523 WHERE (id1 = 6249219) AND (id2 = 74354) *** (2) HOLDS THE LOCK(S): RECORD LOCKS space id 8575 page no 286947 n bits 1048 index `id2` of table `deadlock`.`tbl_deadlock` trx id 1795660513 lock_mode X Record lock, heap no 429 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 00012272; asc "r;; 1: len 4; hex 00721f45; asc r E;; Record lock, heap no 430 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 00012272; asc "r;; 1: len 4; hex 00721fe3; asc r ;; Record lock, heap no 431 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 00012272; asc "r;; 1: len 4; hex 0072218f; asc r! ;; ...省略很多 Record lock... *** (2) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 8575 page no 344554 n bits 120 index `PRIMARY` of table `deadlock`.`tbl_deadlock` trx id 1795660513 lock_mode X locks rec but not gap waiting Record lock, heap no 9 PHYSICAL RECORD: n_fields 44; compact format; info bits 0 0: len 4; hex 00722663; asc r&c;; ...省略無關的兩行... 3: len 4; hex 005f5434; asc _T4;; 4: len 4; hex 00012272; asc "r;; ...省略很多行... *** WE ROLL BACK TRANSACTION (1) ...省略...
Part2 原因分析
首先簡單了解一下死鎖的幾個要素:
1. 互斥條件:一個資源每次只能被一個進程占用。
MySQL 的鎖機制天然具備這個條件。
2. 請求與保持條件:資源請求被阻塞時,已持有的資源不會被釋放。
MySQL 不觸發死鎖回滾,且未進入 lockwait_timeout 的時候,具備這個條件。
3. 不剝奪條件:已獲得的資源,在末使用完之前,不能強行剝奪。
MySQL 的鎖機制天然具備這個條件。
4. 循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關系,通常會表現為有向環。
由于 MySQL 的鎖機制的原因,只需要判斷出兩個 SQL 語句的鎖存在循環等待,那么死鎖的條件就會成立了。
接下來對 MySQL 記錄的死鎖信息進行詳細的分析,首先觀察死鎖的事務詳情這一部分信息:
LOCK WAIT 4 lock struct(s), heap size 1184, 3 row lock(s)。...... 20 lock struct(s), heap size 2936, 40 row lock(s)
可以很明顯可以發現,這兩個語句涉及到的數據行還是比較多的,用戶的疑問:數據更新的并不是同一行,其實是個誤解。那么理論上,“循環等待:互相持有對方需要的鎖”,這種典型的死鎖場景是可能會存在的。
接下來,重點放在更細節的信息上:
*** (1) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 8575 page no 286947 n bits 1048 index `id2` of table `deadlock`.`tbl_deadlock` trx id 1795660514 lock_mode X waiting Record lock, heap no 429 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 00012272; asc "r;; 1: len 4; hex 00721f45; asc r E;; ...... *** (2) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 8575 page no 344554 n bits 120 index `PRIMARY` of table `deadlock`.`tbl_deadlock` trx id 1795660513 lock_mode X locks rec but not gap waiting Record lock, heap no 9 PHYSICAL RECORD: n_fields 44; compact format; info bits 0 0: len 4; hex 00722663; asc r&c;; ...省略無關的兩行... 3: len 4; hex 005f5434; asc _T4;; 4: len 4; hex 00012272; asc "r;; ...省略很多行...
用戶提出的疑問:使用的也是不同的索引,為什么會發送死鎖?實際上二級索引上的記錄鎖,最終也會加到主鍵上。
這個很好理解,如果二級索引上,通過搜索商品表的商品名稱索引(二級索引)搜索“iphone12”,并給這一行數據加上了鎖,鎖住了“iphone12”這個商品的詳情數據行,如果別的事務可以通過搜索主鍵來修改這一行數據,明顯是不行的。
因此本案例中,雖然死鎖信息中記錄的索引名稱不一樣,但是鎖爭用的條件是成立的,即:trx1 通過二級索引向主鍵上執行了加鎖操作,而 trx2 在其他的二級索引上拿到了鎖,但是主鍵鎖拿不到,因此進入了等待狀態。所以只需要定位到具體鎖的數據,找到循環等待的邏輯關系,就可以完成整個案例分析了。
參考上文引用的信息,具體發生死鎖的行的信息都記錄在類似0: len 4; hex 00722663; asc r&c;;的信息中。
trx1 記錄的鎖等待信息是二級索引 id2,因為 id2 是一個單行索引,因此只會有 0 和 1 兩行信息,0 代表的就是具體的行 id2,1 即為主鍵。通過 16 進制轉換工具,轉成 10 進制,可以發現對應的數據如下:
pk = 7479109 and id2 = 74354
那么再看看 trx2 記錄的信息,鎖等待方面,記錄的信息是主鍵,所以這個地方會有完整的表數據,過濾掉無效的數據之后,留下了三行:0 為主鍵,3 為 id1,4 為 id2。轉換進制之后,對應的數據如下:
pk = 7480931 and id1 = 6247476 and id2 = 74354
可以看到,trx2 等待的鎖,id1 和 id2 剛好滿足 trx1 的查詢條件。而 trx2 持有的鎖信息中,第一個剛好就是 trx1 等待的:
trx2 持有的鎖
那么關于這個死鎖案例的具體場景,就可以用下有向環的圖例進行說明:
死鎖圖例
至此為止,這個死鎖的案例分析就完成了,從最初的死鎖成立條件分析,到解讀具體的鎖內容,最終完成了死鎖的有向環圖例。
實際上,自己觀察一下這個死鎖的有向環圖例,會發現這兩個語句用到了兩個單列索引,那么進一步思考的話,如果這兩個列建成了聯合索引,這個死鎖的案例是不是就可能不會發生了?
Part3 總結
對于死鎖的問題,只需要根據四個條件,一步一步過濾與分析,通過解讀死鎖現場的詳細內容,就可以準確的還原整個死鎖的發生原因以及涉及到的數據行。當然,在實際的業務環境中,可能還會有更復雜和隱蔽的死鎖案例,但是不論多么隱蔽和復雜,死鎖分析的思路和步驟都是相似的。
感謝各位的閱讀,以上就是“死鎖是怎么產生的”的內容了,經過本文的學習后,相信大家對死鎖是怎么產生的這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。