SQL-mysql鎖等待與死鎖


一 前言

本篇是MYSQL高級進階篇內容第二篇,學習本篇的基礎是知識追尋者之前發布過的文章,尤其是《MYSQL架構入門篇》重中之重;

《SQL-你真的了解什么SQL么?》

《SQL-小白最佳入門sql查詢一》

《SQL-小白最佳入門sql查詢二》

《SQL- 多年開發人員都不懂的插入與更新刪除操作注意點》

《SQL-SQL事物操作》

《SQL-Mysql數據類型》

《SQL-mysql視圖的前世今生》

《SQL-mysql儲存過程》

《SQL-mysql游標與觸發器》

《SQL-mysql用戶權限管理》

《SQL-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 如何避免死鎖

  • 在死鎖容易產生得表使用表鎖不會產生死鎖;
  • 避免交叉使用相同的鎖


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM