MySQL死鎖使用詳解及檢測和避免方法
前言
上一篇博客我們知道的Mysql事務(wù)的隔離機(jī)制和實(shí)現(xiàn),以及鎖的詳細(xì)解析
鏈接: MySQL臟讀幻讀不可重復(fù)讀及事務(wù)的隔離級(jí)別和MVCC、LBCC實(shí)現(xiàn)
在我們使用鎖的時(shí)候,有一個(gè)問題是需要注意和避免的,我們知道,排它鎖有互斥的特性。一個(gè)事務(wù)或者說一個(gè)線程持有鎖的時(shí)候,會(huì)阻止其他的線程獲取鎖,這個(gè)時(shí)候會(huì)造成阻塞等待,如果循環(huán)等待,會(huì)有可能造成死鎖。
這個(gè)問題我們需要從幾個(gè)方面來分析,一個(gè)是鎖為什么不釋放,第二個(gè)是被阻塞了怎么辦,第三個(gè)死鎖是怎么發(fā)生的,怎么避免。
鎖的釋放與阻塞
回顧:鎖什么時(shí)候釋放?
事務(wù)結(jié)束(commit,rollback)﹔
客戶端連接斷開。
如果一個(gè)事務(wù)一直未釋放鎖,其他事務(wù)會(huì)被阻塞多久?會(huì)不會(huì)永遠(yuǎn)等待下去?
如果是,在并發(fā)訪問比較高的情況下,如果大量事務(wù)因無法立即獲得所需的鎖而掛起,會(huì)占用大量計(jì)算機(jī)資源,造成嚴(yán)重性能問題,甚至拖跨數(shù)據(jù)庫。
線上怕不怕這個(gè)錯(cuò)?
[Err] 1205 - Lock wait timeout exceeded; try restarting transaction
MySQL有一個(gè)參數(shù)來控制獲取鎖的等待時(shí)間,默認(rèn)是50秒。
show VARIABLES like "innodb_lock_wait_timeout";
對(duì)于死鎖,是無論等多久都不能獲取到鎖的,這種情況,也需要等待50秒鐘嗎?那不是白白浪費(fèi)了50秒鐘的時(shí)間嗎?
死鎖的發(fā)生和檢測
演示一下,開兩個(gè)會(huì)話:
方便對(duì)時(shí)間線的提現(xiàn),這里用圖片,有興趣的可以跟著模仿一下
栗子一:
栗子二:
在第一個(gè)事務(wù)中,檢測到了死鎖,馬上退出了,第二個(gè)事務(wù)獲得了鎖,不需要等待50秒:
[Err] 1213 - Deadlock found when trying to get lock; try restarting transaction
為什么可以直接檢測到呢?是因?yàn)樗梨i的發(fā)生需要滿足一定的條件,對(duì)于我們程序員來說,有明確的條件,意味著能判定,所以在發(fā)生死鎖時(shí),InnoDB一般都能通過算法(wait-for graph)自動(dòng)檢測到。
那么死鎖需要滿足什么條件?死鎖的產(chǎn)生條件,因?yàn)殒i本身是互斥的:
- (1)同一時(shí)刻只能有一個(gè)事務(wù)持有這把鎖;
- (2)其他的事務(wù)需要在這個(gè)事務(wù)釋放鎖之后才能獲取鎖,而不可以強(qiáng)行剝奪;
- (3)當(dāng)多個(gè)事務(wù)形成等待環(huán)路的時(shí)候,即發(fā)生死鎖。
理發(fā)店有兩個(gè)總監(jiān)。一個(gè)負(fù)責(zé)剪頭的Tony老師,一個(gè)負(fù)責(zé)洗頭的Kelvin老師。Tony老師不能同時(shí)給兩個(gè)人剪頭,這個(gè)就叫互斥
。
Tony在給別人在剪頭的時(shí)候,你不能讓他停下來幫你剪頭,這個(gè)叫不能強(qiáng)行剝奪
。
如果Tony的客戶對(duì)Kelvin說:你不幫我洗頭我怎么剪頭? Kelvin 的客戶對(duì)Tony說:你不幫我剪頭我怎么洗頭?這個(gè)就叫形成等待環(huán)路
。
實(shí)際上,發(fā)生死鎖的情況非常多,但是都滿足以上3個(gè)條件。
這個(gè)也是表鎖是不會(huì)發(fā)生死鎖的原因,因?yàn)楸礞i的資源都是一次性獲取的。
如果鎖一直沒有釋放,就有可能造成大量阻塞或者發(fā)生死鎖,造成系統(tǒng)吞吐量下降,這時(shí)候就要查看是哪些事務(wù)持有了鎖。
查看鎖信息(日志)
首先,SHow STATUS命令中,包括了一些行鎖的信息:
show status like 'innodb_row_lock_%';
lnnodb_row_lock_current_waits:當(dāng)前正在等待鎖定的數(shù)量;
lnnodb_row_lock_time :從系統(tǒng)啟動(dòng)到現(xiàn)在鎖定的總時(shí)間長度,單位ms;
Innodb_row_lock_time_avg :每次等待所花平均時(shí)間;
Innodb_row_lock_time_max:從系統(tǒng)啟動(dòng)到現(xiàn)在等待最長的一次所花的時(shí)間;
lnnodb_row_lock_waits :從系統(tǒng)啟動(dòng)到現(xiàn)在總共等待的次數(shù)。
SHOW命令是一個(gè)概要信息。InnoDB還提供了三張表來分析事務(wù)與鎖的情況:
select * from information_schema.INNODB_TRX; --當(dāng)前運(yùn)行的所有事務(wù)﹐還有具體的語句
select* from information_schema.INNODB_LOCKS; --當(dāng)前出現(xiàn)的鎖
select * from information_schema.INNODB_LOCK_WAITS; --鎖等待的對(duì)應(yīng)關(guān)系
更加詳細(xì)的鎖信息,開啟標(biāo)準(zhǔn)監(jiān)控和鎖監(jiān)控:
額外的監(jiān)控肯定會(huì)消耗額外的性能
set GLOBAL innodb_status_output=ON; set GLOBAL innodb_status_output_locks=ON;
通過分析鎖日志,找出持有鎖的事務(wù)之后呢?
如果一個(gè)事務(wù)長時(shí)間持有鎖不釋放,可以kill事務(wù)對(duì)應(yīng)的線程ID,也就是INNODB_TRX表中的trx_mysql_thread_id,例如執(zhí)行kill 4,kill 7, kill 8。
當(dāng)然,死鎖的問題不能每次都靠kill線程來解決,這是治標(biāo)不治本的行為。我們應(yīng)該盡量在應(yīng)用端,也就是在編碼的過程中避免。
有哪些可以避免死鎖的方法呢?
死鎖的避免
- 1、在程序中,操作多張表時(shí),盡量以相同的順序來訪問(避免形成等待環(huán)路)
- 2、批量操作單張表數(shù)據(jù)的時(shí)候,先對(duì)數(shù)據(jù)進(jìn)行排序(避免形成等待環(huán)路);
- 3、申請(qǐng)足夠級(jí)別的鎖,如果要操作數(shù)據(jù),就申請(qǐng)排它鎖;
- 4、盡量使用索引訪問數(shù)據(jù),避免沒有where條件的操作,避免鎖表;
- 5、如果可以,大事務(wù)化成小事務(wù);
- 6、使用等值查詢而不是范圍查詢查詢數(shù)據(jù),命中記錄,避免間隙鎖對(duì)并發(fā)的影響。
到此這篇關(guān)于MySQL死鎖使用詳解及檢測和避免方法的文章就介紹到這了,更多相關(guān)MySQL死鎖 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mysql多表join時(shí)候update更新數(shù)據(jù)的方法
如果item表的name字段為''就用resource_library 表的resource_name字段前面加上字符串Review更新它,他們的關(guān)聯(lián)關(guān)系在表resource_review_link中。2011-03-03一次MySQL啟動(dòng)導(dǎo)致的事故實(shí)戰(zhàn)記錄
這篇文章主要給大家介紹了一次MySQL啟動(dòng)導(dǎo)致的事故實(shí)戰(zhàn)記錄,記錄了MySQL 啟動(dòng)成功但未監(jiān)聽端口的解決方法,文中給出了詳細(xì)的解決方法,需要的朋友可以參考下2021-09-09實(shí)現(xiàn)MySQL數(shù)據(jù)庫鎖的兩種方式
今天我們就來聊一聊數(shù)據(jù)庫的鎖,實(shí)現(xiàn)數(shù)據(jù)庫鎖的兩種方式,一個(gè)是實(shí)現(xiàn)樂觀鎖的方式,一個(gè)是實(shí)現(xiàn)悲觀鎖的實(shí)現(xiàn)方式,文中的代碼示例介紹的非常詳細(xì),需要的朋友可以參考下2023-06-06mysql創(chuàng)建學(xué)生表、課程表及學(xué)生選課表詳細(xì)代碼
這篇文章主要給大家介紹了mysql創(chuàng)建學(xué)生表、課程表及學(xué)生選課表的相關(guān)資料,學(xué)生、課程以及成績的增刪改查都是建立在連接數(shù)據(jù)庫的基礎(chǔ)之上,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12MySQL示例講解數(shù)據(jù)庫約束以及表的設(shè)計(jì)
約束主要完成對(duì)數(shù)據(jù)的檢驗(yàn),保證數(shù)據(jù)庫數(shù)據(jù)的完整性;如果有相互依賴數(shù)據(jù),保證該數(shù)據(jù)不被刪除,本篇文章教你如何給表設(shè)置約束及設(shè)計(jì)2022-06-06mysql count詳解及函數(shù)實(shí)例代碼
這篇文章主要介紹了mysql count詳解及函數(shù)實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-01-01