mysql死鎖的解讀(轉載)

死鎖(Deadlock)


什麼是死鎖

所謂死鎖:是指兩個或兩個以上的進程在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。由於資源佔用是互斥的,當某個進程提出申請資源後,使得有關進程在無外力協助下,永遠分配不到必需的資源而無法繼續運行,這就產生了一種特殊現象死鎖。

產生死鎖的四個必要條件:

(1) 互斥條件:一個資源每次只能被一個進程使用。
(2) 請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
(3) 不剝奪條件:進程已獲得的資源,在末使用完之前,不能強行剝奪。
(4) 循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關係。

在工作當中可能會遇到數據庫CPU等各項指標異常,查看數據庫所有進程發現絕大部分查詢均處於等待中,那麼可以判定應該是遇到了死鎖,如下圖所示:

SHOW FULL PROCESSLIST;

圖片描述

死鎖的影響

當產生某表死鎖的一開始,所有涉及這張表的操作都將受到阻塞。假設這張表在業務邏輯上是讀寫頻繁的,那就會使很多操作在那裏排隊等待,而排隊等待會佔用數據庫連接,當該達到該數據庫連接數的最大承載數之後,就會使所有數據庫操作均無法再繼續下去,致使數據庫各項指標異常,導致整個環境崩潰。在生產環境中出現這種問題,那是相當致命的,當發現數據庫指標異常時因快速處理!

如何發現死鎖

1.查詢數據庫進程

主要看State字段,如果出現大量 waiting for ..lock 即可判定死鎖:

SHOW FULL PROCESSLIST;

注意:需要擁有root組權限(supper),否則只能看到當前用戶的進程,無法查詢所有

2.查看當前的事務

SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;

圖片描述

INNODB_TRX 表包含信息關於每個事務(排除只讀事務)當前執行的在InnoDB,包含是否事務是等待一個鎖, 當事務啓動後, SQL語句事務是正在執行

INNODB_TRX Columns 相關列信息:

a) trx_id:innodb存儲引擎內部事務唯一的事務id。

b) trx_state:當前事務的狀態。

c) trx_started:事務開始的時間。

d) trx_requested_lock_id:等待事務的鎖id,如trx_state的狀態爲LOCK WAIT,那麼該值代表當前事務之前佔用鎖資源的id,如果trx_state不是LOCK WAIT的話,這個值爲null。

e) trx_wait_started:事務等待開始的時間。

f) trx_weight:事務的權重,反映了一個事務修改和鎖住的行數。在innodb的存儲引擎中,當發生死鎖需要回滾時,innodb存儲引擎會選擇該值最小的事務進行回滾。

g) trx_mysql_thread_id:正在運行的mysql中的線程id,show full processlist顯示的記錄中的thread_id。

h) trx_query:事務運行的sql語句,在實際中發現,有時會顯示爲null值,當爲null的時候,就是t2事務中等待鎖超時直接報錯(ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction)後,trx_query就顯示爲null值

比如事務t2正在運行trx_query: update test.t1 set b='t2' where a=1的sql語句,t1先執行,所以是trx_state: RUNNING先申請的資源一直在運行,而t2後run的所以是trx_state: LOCK WAIT一直在等待t1執行完後釋放資源。 但是並不能仔細判斷鎖的一些詳細情況,我們需要再去看當前鎖定的事務表數據。

3.查看當前鎖定的事務

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;

INNODB_LOCKS 表包含信息關於每個鎖一個InnoDB 事務已經請求,但是沒有獲得鎖,每個lock一個事務持有是堵塞另外一個事務

INNODB_LOCKS Columns 相關列信息:

a) lock_id:鎖的id以及被鎖住的空間id編號、頁數量、行數量

b) lock_trx_id:鎖的事務id。

c) lock_mode:鎖的模式。

d) lock_type:鎖的類型,表鎖還是行鎖

e) lock_table:要加鎖的表。

f) lock_index:鎖的索引。

g) lock_space:innodb存儲引擎表空間的id號碼

h) lock_page:被鎖住的頁的數量,如果是表鎖,則爲null值。

i) lock_rec:被鎖住的行的數量,如果表鎖,則爲null值。

j) lock_data:被鎖住的行的主鍵值,如果表鎖,則爲null值。

如以下查詢 :

mysql> select * from INNODB_LOCKSG

1. row **

lock_id: 3015646:797:3:2

lock_trx_id: 3015646

lock_mode: X

lock_type: RECORD

lock_table: test.t1

lock_index: PRIMARY

lock_space: 797

lock_page: 3

lock_rec: 2

lock_data: 1

2. row **

lock_id: 3015645:797:3:2

lock_trx_id: 3015645

lock_mode: X

lock_type: RECORD

lock_table: test.t1

lock_index: PRIMARY

lock_space: 797

lock_page: 3

lock_rec: 2

lock_data: 1

2 rows in set (0.00 sec)

這裏我們可以看到當前的鎖信息了,2個事務都鎖定了,看相同的數據lock_space: 797、lock_page: 3、lock_rec: 2可以得出事務t1和事務t2訪問了相同的innodb數據塊,再通過lock_data字段信息lock_data: 1,看到鎖定的數據行都是主鍵爲1的數據記錄,可見2個事務t1和t2都申請了相同的資源,因此會被鎖住,事務在等待。

通過lock_mode: X值也可以看出事務t1和t2申請的都是排它鎖。

PS:當執行範圍查詢更新的時候,這個lock_data的值並非是完全準確。當我們運行一個範圍更新時,lock_data只返回最先找到的第一行的主鍵值id;另外如果當前資源被鎖住了,與此同時由於鎖住的頁因爲InnoDB存儲引擎緩衝池的容量,而導致替換緩衝池頁面,再去查看INNODB_LOCKS表時,這個lock_data會顯示未NULL值,意味着InnoDB存儲引擎不會從磁盤進行再一次查找。

4.查看當前等鎖的事務

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;

INNODB_LOCK_WAITS 表包含了blocked的事務的鎖等待的狀態。當事務量比較少,我們可以直觀的查看,當事務量非常大,鎖等待也時常發生的情況下,這個時候可以通過INNODB_LOCK_WAITS表來更加直觀的反映出當前的鎖等待情況:

INNODB_LOCK_WAITSColumns 相關列信息:

a) requesting_trx_id:申請鎖資源的事務id。

b) requested_lock_id:申請的鎖的id。

c) blocking_trx_id:阻塞的事務id。

d) blocking_lock_id:阻塞的鎖的id。

如以下查詢:
mysql> select * from INNODB_LOCK_WAITSG

1. row **

requesting_trx_id: 3015646

requested_lock_id: 3015646:797:3:2

blocking_trx_id: 3015645

blocking_lock_id: 3015645:797:3:2

1 row in set (0.00 sec)

mysql>

這裏我們可以看到事務t1(3015646)申請了鎖資源,而事務t2(3015645)則阻塞了事務t1的申請。

如何處理死鎖

殺死進程

通過以上方法一可以查詢對應死鎖的數據庫進程,可以直接殺掉

kill 進程ID

如果系統資源充足,進程的資源請求都能夠得到滿足,死鎖出現的可能性就很低,否則就會因爭奪有限的資源而陷入死鎖。其次,進程運行推進順序與速度不同,也可能產生死鎖。

雖然不能完全避免死鎖,但可以使死鎖的數量減至最少。將死鎖減至最少可以增加事務的吞吐量並減少系統開銷,因爲只有很少的事務回滾,而回滾會取消事務執行的所有工作。由於死鎖時回滾而由應用程序重新提交。

下列方法有助於最大限度地降低死鎖:

(1)按同一順序訪問對象。
(2)避免事務中的用戶交互。
(3)保持事務簡短並在一個批處理中。
(4)使用低隔離級別。
(5)使用綁定連接。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章