MySQL死鎖使用詳解及偵測與避免方法

WBOY
發布: 2022-09-09 13:43:56
轉載
2563 人瀏覽過

推薦學習:mysql影片教學

#在我們使用鎖的時候,有一個問題是需要注意和避免的,我們知道,排它鎖有互斥的特性。一個事務或說一個執行緒持有鎖的時候,會阻止其他的執行緒取得鎖,這個時候會造成阻塞等待,如果循環等待,會有可能造成死鎖。

這個問題我們需要從幾個方面來分析,一個是鎖為什麼不釋放,第二個是被阻塞了怎麼辦,第三個死鎖是怎麼發生的,怎麼避免。

鎖定的釋放與阻塞

回顧:鎖定何時釋放?
交易結束(commit,rollback)﹔
客戶端連線中斷。

如果一個事務一直未釋放鎖,其他事務會被阻塞多久?會不會永遠等待下去?
如果是,在並發訪問比較高的情況下,如果大量事務因無法立即獲得所需的鎖而掛起,會佔用大量電腦資源,造成嚴重效能問題,甚至拖跨資料庫。

線上怕不怕這個錯?

[Err] 1205 - Lock wait timeout exceeded; try restarting transaction
登入後複製

MySQL有一個參數可以控制取得鎖定的等待時間,預設是50秒。

show VARIABLES like "innodb_lock_wait_timeout";
登入後複製

對於死鎖,是無論等多久都不能獲取到鎖的,這種情況,也需要等待50秒鐘嗎?那不是白白浪費了50秒鐘的時間嗎?

死鎖的發生與偵測

示範一下,開兩個會話:

#方便對時間軸的提現,這裡用圖片,有興趣的可以跟著模仿一下

栗子一:

#栗子二:

在第一個事務中,偵測到了死鎖,馬上退出了,第二個事務獲得了鎖,不需要等待50秒:

[Err] 1213 - Deadlock found when trying to get lock; try restarting transaction
登入後複製

為什麼可以直接偵測到呢?是因為死鎖的發生需要滿足一定的條件,對於我們程式設計師來說,有明確的條件,意味著能判定,所以在發生死鎖時,InnoDB一般都能透過演算法(wait-for graph)自動偵測到。

那麼死鎖需要滿足什麼條件?死鎖的產生條件,因為鎖本身是互斥的:

  • (1)同一時刻只能有一個事務持有這把鎖;
  • (2)其他的事務需要在這個事務釋放鎖之後才能取得鎖,而不可以強行剝奪;
  • (3)當多個事務形成等待環路的時候,即發生死鎖。

理髮店有兩位總監。一個負責剪頭的Tony老師,一個負責洗頭的Kelvin老師。 Tony老師不能同時給兩個人剪頭,這個就叫互斥

Tony在給別人在剪頭的時候,你不能讓他停下來幫你剪頭,這個叫不能強行剝奪
如果Tony的客戶對Kelvin說:你不幫我洗頭我怎麼剪頭? Kelvin 的客戶對Tony說:你不幫我剪頭我怎麼洗頭?這個就叫形成等待環路
實際上,發生死鎖的情況非常多,但是都滿足以上3個條件。
這個也是表鎖是不會發生死鎖的原因,因為表鎖的資源都是一次取得的

如果鎖一直沒有釋放,就有可能造成大量阻塞或發生死鎖,造成系統吞吐量下降,這時候就要查看是哪些事務持有了鎖。

查看鎖定資訊(日誌)

首先,SHow STATUS指令中,包含了一些行鎖的資訊:

show status like 'innodb_row_lock_%';
登入後複製

lnnodb_row_lock_current_waits:目前正在等待鎖定的數量;
lnnodb_row_lock_time :從系統啟動到現在鎖定的總時間長度,單位ms;
Innodb_row_lock_time_avg :每次等待所花平均時間;
Innodb_row_lock_time_max:從系統啟動到現在等待最長的一次所花的時間;
lnnodb_row_lock_waits :從系統啟動到現在總共等待的次數。

SHOW指令是一個概要資訊。 InnoDB也提供了三張表來分析交易與鎖定的情況:

select * from information_schema.INNODB_TRX; --当前运行的所有事务﹐还有具体的语句
登入後複製

#
select* from information_schema.INNODB_LOCKS; --当前出现的锁
登入後複製

select * from information_schema.INNODB_LOCK_WAITS; --锁等待的对应关系
登入後複製

更加详细的锁信息,开启标准监控和锁监控:

额外的监控肯定会消耗额外的性能

set GLOBAL innodb_status_output=ON;
set GLOBAL innodb_status_output_locks=ON;
登入後複製

通过分析锁日志,找出持有锁的事务之后呢?
如果一个事务长时间持有锁不释放,可以kill事务对应的线程ID,也就是INNODB_TRX表中的trx_mysql_thread_id,例如执行kill 4,kill 7, kill 8。
当然,死锁的问题不能每次都靠kill线程来解决,这是治标不治本的行为。我们应该尽量在应用端,也就是在编码的过程中避免。
有哪些可以避免死锁的方法呢?

死锁的避免

  • 1、在程序中,操作多张表时,尽量以相同的顺序来访问(避免形成等待环路)
  • 2、批量操作单张表数据的时候,先对数据进行排序(避免形成等待环路);
  • 3、申请足够级别的锁,如果要操作数据,就申请排它锁;
  • 4、尽量使用索引访问数据,避免没有where条件的操作,避免锁表;
  • 5、如果可以,大事务化成小事务;
  • 6、使用等值查询而不是范围查询查询数据,命中记录,避免间隙锁对并发的影响。

推荐学习:mysql视频教程

以上是MySQL死鎖使用詳解及偵測與避免方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:jb51.net
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板