一 前言
本篇是MYSQL高級進階篇內容第二篇,學習本篇的基礎是知識追尋者之前發布過的文章,尤其是《MYSQL架構入門篇》重中之重;
公眾號:知識追尋者
知識追尋者(Inheriting the spirit of open source, Spreading technology knowledge;)
二 鎖等待
鎖等待的意思非常好理解,就是session (事物會話,開啟一個事物代表一個會話)A 對 某行數據獲取獨占鎖(在這邊一般就是寫鎖),然后session B 對相同的行進行獲取獨占鎖就發生了鎖等待;簡單理解就是 小孩子搶玩具,誰先搶到 誰 先玩,沒搶到的玩具的孩子只能 等待 搶到玩具孩子玩膩了再給你,瞬間淚奔有木有,就是這么殘酷,當然MYSQL 沒 這么殘忍 其 還是有一個保留參數 innodb_lock_wait_timeout
指定死鎖 的時間,如果超過 死鎖等待時間就是報異常;
知識追尋者 做個實驗:
session A 執行如下語句,開啟事物,更新索引為1 的語句;此時 session A 獲取了 id= 1 這條 語句的 寫鎖權限;
BEGIN;
update `order` set `year`= '2022' where id = '1';
session B 執行如下 語句 , 跟 上面的語句一樣 ,由於 id =1 這條數據的寫鎖已經被session A 獲取,故會發生鎖等待的情況;
BEGIN;
update `order` set `year`= '2022' where id = '1';
知識追尋者這邊默認等待了50秒 就報了如下異常
Lock wait timeout exceeded; try restarting transaction
查看 默認鎖等待 語句
show VARIABLES like 'innodb_lock_wait_timeout'
三 死鎖
3.1 死鎖的產生
死鎖 就是 兩個以上的會話 在 搶占 資源過程中 ,產生相互等待的情況;有點繞是不是,其實很簡單 死鎖是建立在 鎖等待的基礎上,session A 獲取 id = 1 的寫鎖 , session B 獲取 id =2 的寫鎖 ,此時由於索引不同,故不會長生鎖等待現象 ; 當 session A 嘗試 獲取 id =2 的 寫鎖時 ,由於 id = 2 寫鎖已經被 session B 獲取 ,故產生鎖等待;當 session B 嘗試 獲取 id = 1 的寫鎖時 ,由於id =1 寫鎖已經被 session A 獲取, 此時 產生鎖等待; 由於 session A 與 session B 同時 都在 鎖 等待狀態,產生了等待對方釋放鎖,故會產生死鎖;
知識追尋者做個試驗
session A 執行語句, 獲取 id =1 的 寫鎖權限;
BEGIN;
update `order` set `year`= '2022' where id = '1';
session B 執行語句, 獲取 id =2 的 寫鎖權限;
BEGIN;
update `order` set `year`= '2022' where id = '2';
session A 執行語句, 嘗試獲取 id =2 的 寫鎖權限,進入鎖等待狀態
update `order` set `year`= '2022' where id = '2';
session B 執行語句, 嘗試獲取 id =1 的 寫鎖權限,進入鎖等待狀態
update `order` set `year`= '2022' where id = '1';
當 B 進入 鎖等待后就直接報死鎖異常
Deadlock found when trying to get lock; try restarting transaction
3.2 查看死鎖
可以使用 show engine innodb status
查看死鎖
......
*** (1) TRANSACTION: // 事物A
TRANSACTION 253507, ACTIVE 474 sec starting index read
mysql tables in use 1, locked 1 // 已經使用一個鎖
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 17001, OS thread handle 139824777217792, query id 2191731 ......
root updating
update `order` set `year`= '2022' where id = '2'//執行得語句
*** (1) WAITING FOR THIS LOCK TO BE GRANTED: // 等待鎖釋放獲取鎖
RECORD LOCKS space id 65 page no 3 n bits 80 index PRIMARY of table `zszxz`.`order` trx id 253507 lock_mode X locks rec but not gap waiting
.....
*** (2) TRANSACTION: // 事物 B
TRANSACTION 253508, ACTIVE 425 sec starting index read
mysql tables in use 1, locked 1 // 已經使用一個鎖
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 17002, OS thread handle 139824778569472, query id 2191735 ......
root updating
update `order` set `year`= '2022' where id = '1'//執行得語句
*** (2) HOLDS THE LOCK(S): //持有鎖
RECORD LOCKS space id 65 page no 3 n bits 80 index PRIMARY of table `zszxz`.`order` trx id 253508 lock_mode X locks rec but not gap
......
*** (2) WAITING FOR THIS LOCK TO BE GRANTED: // 等待鎖釋放獲取鎖
RECORD LOCKS space id 65 page no 3 n bits 80 index PRIMARY of table `zszxz`.`order` trx id 253508 lock_mode X locks rec but not gap waiting
......
不得不說下字母代表鎖得類型如下
- 共享鎖(S)
- 排他鎖(X)
- 意向共享(IS)
- 意向排他(IX)
- gap lock(GK), 間隙鎖,鎖定一個范圍,不包括當前記錄本身;
- RECORD LOCKS 代表記錄鎖;
可以看見上面得語句 (1) 代表 事物A ,MYSQL 線程id 17001 ; (2) 代表事物B, MYSQL 線程id 17002 ; 事物 A 與B 都在等待 對方釋放鎖 ,產生了死鎖;
Tip; 查看表鎖 : show status like 'table%';
如何解決死鎖,知識追尋者這邊給個思路:
查找到死鎖線程,殺死MYSQL死鎖的線程(kill命令);
如果事物未提交,直接回滾事物;
3.3 如何避免死鎖
- 在死鎖容易產生得表使用表鎖不會產生死鎖;
- 避免交叉使用相同的鎖