總結線上遇到的MySQL死鎖問題
阿新 • • 發佈:2018-12-25
線上遇到了MySQL死鎖的相關問題,需要檢視MySQL出現的Deadlock日誌,可以通過執行:
注意:各個具體資料庫並不一定完全實現上述4個隔離級別。
InnoDB的行鎖模式以及加鎖方法
共享鎖:允許一個事務去讀一行,阻止其他事務獲得相同資料集的排他鎖;
排他鎖:允許獲得排它鎖的事務更新資料,阻止其他事務取得相同資料集的共享讀鎖和排他寫鎖;
意向共享鎖:事務打算給資料行加行共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的意向共享鎖;
意向排他鎖:事務打算給資料行加行排他鎖,事務在給一個數據行加排它鎖前必須先取得該表的意向排他鎖。
意向鎖是InnoDB自動加的,不需要使用者干預,對於update, insert, delete語句InnoDB會自動給涉及資料集加排他鎖,對於普通select語句InnoDB不會加任何鎖,事務可以通過以下語句顯式地給記錄集加上共享鎖和排他鎖。
show engine innodb status
來檢視innodb型別資料庫的狀態,查詢laster detected deadlock部分,可以看到最近造成死鎖的兩條sql
------------------------
LATEST DETECTED DEADLOCK
------------------------
161020 17:58:11
*** (1) TRANSACTION:
TRANSACTION ED354BF4, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 6 lock struct(s), heap size 1248, 3 row lock(s), undo log entries 1
MySQL thread id 2938474, OS thread handle 0x2b9ffd19b940, query id 3121991643 192.168.1.163 apitest140715 Updating
UPDATE xxx SET fix_stock=fix_stock+-1 WHERE aaa = 1 AND aaa=101488 AND fix_stock+-1>=0 AND stock>=fix_stock+-1
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 196984 page no 743 n bits 1272 index `xxxx` of table `xxx`.`xxxx` trx id ED354BF4 lock_mode X waiting
Record lock, heap no 581 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80018c70; asc p;;
1: len 4; hex 80018ce8; asc ;;
*** (2) TRANSACTION:
TRANSACTION ED354C8C, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
6 lock struct(s), heap size 1248, 4 row lock(s)
MySQL thread id 2938340, OS thread handle 0x2b9ffcae8940, query id 3121991660 192.168.1.115 163test Updating
update xxx
set fix_stock=fix_stock+1
where product_spec_id=101488
and fix_stock+1>=0
and stock>=fix_stock+1
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 196984 page no 743 n bits 1272 index `xxx` of table `shop_zp`.`gt_goods_warehouse_index` trx id ED354C8C lock_mode X
Record lock, heap no 581 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80018c70; asc p;;
1: len 4; hex 80018ce8; asc ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 196974 page no 2114 n bits 176 index `PRIMARY` of table `xxxx`.`xxxx` trx id ED354C8C lock_mode X locks rec but not gap waiting
Record lock, heap no 85 PHYSICAL RECORD: n_fields 35; compact format; info bits 0
0: len 4; hex 00018c70; asc p;;
1: len 6; hex 0000ed354bf4; asc 5K ;;
2: len 7; hex 7600011487203d; asc v =;;
3: len 1; hex 00; asc ;;
4: len 4; hex 80000000; asc ;;
5: len 1; hex 00; asc ;;
6: len 0; hex ; asc ;;
7: len 12; hex 373030323335343037303836; asc 700235407086;;
8: len 0; hex ; asc ;;
9: len 4; hex 0010225f; asc "_;;
10: len 4; hex 00000338; asc 8;;
11: len 4; hex 800186a0; asc ;;
12: len 4; hex 80000056; asc V;;
13: len 4; hex 80000000; asc ;;
14: len 4; hex 80000000; asc ;;
15: len 9; hex 800000000000000577; asc w;;
16: len 5; hex 8000000000; asc ;;
17: len 5; hex 8000000000; asc ;;
18: len 1; hex 81; asc ;;
19: len 9; hex 800000000000000af0; asc ;;
20: len 1; hex 80; asc ;;
21: len 4; hex 0000011b; asc ;;
22: len 4; hex 000000e0; asc ;;
23: len 4; hex 80000000; asc ;;
24: len 4; hex 80000000; asc ;;
25: len 1; hex 81; asc ;;
26: len 4; hex d5684647; asc hFG;;
27: len 4; hex 58089533; asc X 3;;
28: len 0; hex ; asc ;;
29: len 5; hex 800002e505; asc ;;
30: len 5; hex 8000036303; asc c ;;
31: len 4; hex 000f4240; asc [email protected];;
32: len 4; hex 80000000; asc ;;
33: len 4; hex 0000803f; asc ?;;
34: SQL NULL;
*** WE ROLL BACK TRANSACTION (2)
------------
TRANSACTIONS
------------
MySQL鎖機制
相對其他資料庫而言,MySQL的鎖機制比較簡單,其最顯著的特點是不同的儲存引擎支援不同的鎖機制。比如,MyISAM和MEMORY儲存引擎採用的是表級鎖(table-level locking);BDB儲存引擎採用的是頁面鎖(page-level locking),但也支援表級鎖;InnoDB儲存引擎既支援行級鎖(row-level locking),也支援表級鎖,但預設情況下是採用行級鎖。 MySQL這3種鎖的相關指標可以參考:開銷、加鎖速度、死鎖、粒度、併發效能:- 表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的概率最高,併發度最低。
- 行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高。
- 頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度一般。
- 更新丟失(Lost Update):當兩個或多個事務選擇同一行,然後基於最初選定的值更新該行時,由於每個事務都不知道其他事務的存在,就會發生丟失更新問題--最後的更新覆蓋了由其他事務所做的更新。例如,兩個編輯人員製作了同一文件的電子副本。每個編輯人員獨立地更改其副本,然後儲存更改後的副本,這樣就覆蓋了原始文件。最後儲存其更改副本的編輯人員覆蓋另一個編輯人員所做的更改。如果在一個編輯人員完成並提交事務之前,另一個編輯人員不能訪問同一檔案,則可避免此問題。
- 髒讀(Dirty Reads):一個事務正在對一條記錄做修改,在這個事務完成並提交前,這條記錄的資料就處於不一致狀態;這時,另一個事務也來讀取同一條記錄,如果不加控制,第二個事務讀取了這些“髒”資料,並據此做進一步的處理,就會產生未提交的資料依賴關係。這種現象被形象地叫做"髒讀"。
- 不可重複讀(Non-Repeatable Reads):一個事務在讀取某些資料後的某個時間,再次讀取以前讀過的資料,卻發現其讀出的資料已經發生了改變、或某些記錄已經被刪除了!這種現象就叫做“不可重複讀”。
- 幻讀(Phantom Reads):一個事務按相同的查詢條件重新讀取以前檢索過的資料,卻發現其他事務插入了滿足其查詢條件的新資料,這種現象就稱為“幻讀”。
- 讀取資料前,對其加鎖,阻止其他事務對資料進行修改;
- 不用加任何鎖,通過一定機制生成一個數據請求時間點的一致性資料快照,並用這個快照來提供一定級別,也成為多版本併發控制。
讀資料一致性 | 髒讀 | 不可重複讀 | 幻讀 | |
未提交讀 | 最低級別 | 是 | 是 | 是 |
已提交讀 | 語句級 | 否 | 是 | 是 |
可重複讀 | 事務級 | 否 | 否 | 是 |
可序列化 | 最高級別,事務級 | 否 | 否 | 否 |
意向鎖是InnoDB自動加的,不需要使用者干預,對於update, insert, delete語句InnoDB會自動給涉及資料集加排他鎖,對於普通select語句InnoDB不會加任何鎖,事務可以通過以下語句顯式地給記錄集加上共享鎖和排他鎖。
select * from table_name where … lock in share mode; //共享鎖
select * from table_name where … for update; //排它鎖
InnoDB行鎖是通過給索引上的索引項加鎖來實現的,這一點與Oracle不同,後者是通過在資料塊中對相應資料行加鎖實現的,InnoDB這種行鎖實現特點意味著:只有通過索引條件檢索資料,InnoDB才使用行級鎖,否則將使用表鎖!
由於MySQL的行鎖是針對索引加的鎖,不是針對記錄加的鎖,雖然訪問不同行的記錄,但如果使用相同的索引鍵是會出現鎖衝突的。
當表有多個索引的時候,不同的事務可以使用不同的索引鎖定不同的行,不論是使用主鍵索引、唯一鍵索引或是普通索引,InnoDB都會使用行鎖來對資料加鎖。
但有一種特殊情況,即便在條件中使用了索引欄位,是否使用索引來檢索資料是由MySQL通過判斷不同的執行計劃的代價來決定的,如果MySQL認為全表掃描效率更高,比如對一些很小的表,它就不會使用索引,這種情況將會使用表鎖而不是行鎖。因此在分析鎖衝突時,需要檢查SQL執行計劃以確認是否真正使用了索引。
對於InnoDB表,在絕大多數情況下都應該使用行級鎖,因為事務和行鎖往往是我們之所以選擇InnoDB表的理由,但個別特殊事務中,也可以考慮使用表級鎖:
- 當事務需要更新大部分或全部資料,表又比較大,如果使用預設的行鎖,不僅該事務執行效率低,而且可能造成其他事務長時間鎖等待和鎖衝突;
- 事務涉及到多個表,比較複雜,很可能引起死鎖,造成大量事務回滾,這種情況也可以考慮一次性鎖定事務涉及表,從而避免死鎖,減少資料庫因事務回滾帶來的開銷。