資料庫什麼情況會死鎖
死鎖是指當兩個或多個競爭事務彼此等待對方釋放鎖,從而導致事務永遠無法停止的情況,在行鎖級別,死鎖是無法100%避免的。
InnoDB引擎有內部的死鎖探測器,當器發現有死鎖的時候,太會回滾其中一個事務,並會報告立即可見的錯誤,當設計應用程式的時候,我們需要對這種情況準備合理的處理回滾。
關於死鎖的資訊我們可以透過show engine innodb status命令獲取,解決死鎖問題最簡單的一種方法是超時,即當兩個事物互相等待時,當一個等待時間超過了設定的某一個閾值時,其中一個事務進行回滾,另一個繼續進行,在innodb中設定引數 innodb_lock_wait_timeout 來設定超時時間。
超時機制雖然簡單,但是其僅僅透過超時後對事務進行回滾的方式來處理,或者說其是根據FIFO的順序選擇回滾物件,但若超時的事務所佔權重比較大,比如事務處理了很多更新,佔用了很多undo log,這個時候採用簡單的FIFO的方式就不太合適了,因為回滾這個事務的時間相對另一個事務所佔用的時間可能會多很多。
因此除了超時機制,當前資料庫還都普遍採用wait-for grap(等待圖)的方式進行死鎖檢測,比超時的解決方案,這個是一種更加主動的死鎖檢測方式,innoDB儲存引擎也採用這種方案。
wait-for graph 要求資料庫儲存一下兩種資訊:
1。鎖的資訊連結串列
2。事務等待連結串列
透過上述連結串列可以構造一張圖,而在這個圖中如果存在迴路,就代表有死鎖,因此資源相互發生等待,
如上圖,在Transaction wait List 中有4個事務t1,t2,t3,t4, t2 佔用row1
的
x排他鎖,而t1想獲取row1
的
s鎖,這個時候有箭頭從 t1指向row1, 在row2上 t1 和t4 佔有了s共享鎖,而t3 和t4想佔用row2的排他鎖。根據上圖我們可以描繪 wait-for graph 如下
透過上圖我們發現 t1和t2之間有相互依賴情況,會出現死鎖,通常來說Innodb儲存引擎選擇回滾undo代價小的事務。
例子如下:
create table t( a int auto_increment,primary key(a));
insert into t values(1),(2),(3),(4);
啟動事務A 和事務B 分別插入一條資料
事務A:
begin;
insert into t values(null);
select *from t;
事務B
:
begin;
insert into t values(null);
select *from t;
在事務A和事務B分別將自己的id更新為對方的id
事務A:
update t set a=6 where a=5;
事務B:
update t set a=5 where a=6;
我們會發現事務A執行後,sql處於等待狀態, 而執行事務B後馬上就發現死鎖產生
Error Code: 1213。 Deadlock found when trying to get lock; try restarting transaction,
同時我們發現事務A等待結束,併成功提交了。
我們現在可以使用 show engine innoDB status命令來輸出死鎖資訊
show engine innodb status ;
LATEST DETECTED DEADLOCK
————————————
2021-07-13 14:10:58 0x5328
*** (1) TRANSACTION:
TRANSACTION 28227, ACTIVE 372 sec updating or deleting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 2
MySQL thread id 5, OS thread handle 924, query id 325 localhost ::1 goodhope updating
update t set a=6 where a=5
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 303 page no 3 n bits 80 index PRIMARY of table `test`。`t` trx id 28227 lock mode S locks rec but not gap waiting
Record lock, heap no 7 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
0: len 4; hex 80000006; asc ;;
1: len 6; hex 000000006e44; asc nD;;
2: len 7; hex 31000001461137; asc 1 F 7;;
*** (2) TRANSACTION:
TRANSACTION 28228, ACTIVE 318 sec updating or deleting, thread declared inside InnoDB 4999
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 2
MySQL thread id 7, OS thread handle 21288, query id 327 localhost ::1 goodhope updating
update t set a=5 where a=6
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 303 page no 3 n bits 80 index PRIMARY of table `test`。`t` trx id 28228 lock_mode X locks rec but not gap
Record lock, heap no 7 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
0: len 4; hex 80000006; asc ;;
1: len 6; hex 000000006e44; asc nD;;
2: len 7; hex 31000001461137; asc 1 F 7;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 303 page no 3 n bits 80 index PRIMARY of table `test`。`t` trx id 28228 lock mode S locks rec but not gap waiting
Record lock, heap no 6 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
0: len 4; hex 80000005; asc ;;
1: len 6; hex 000000006e43; asc nC;;
2: len 7; hex 30000001fb0110; asc 0 ;;
*** WE ROLL BACK TRANSACTION (2)