MySQL死鎖類deadlock問題排查
認識常用命令
SHOW ENGINE INNODB STATUS;
介紹
SHOW ENGINE INNODB STATUS; 是 InnoDB 存儲引擎提供的一個診斷工具,它會輸出一份非常詳細的報告,包含 InnoDB 內(nèi)部多個維度的狀態(tài)信息。這份報告主要包含以下幾個部分:
- BACKGROUND THREAD: 后臺主線程(如刷新臟頁)的活動信息。
- SEMAPHORES: 信號量信息,用于診斷線程間爭用和鎖等待。如果系統(tǒng)存在大量鎖等待,這里會有體現(xiàn)。
- LATEST DETECTED DEADLOCK: 這是你最關心的部分。它記錄了最近一次發(fā)生的死鎖的詳細信息,包括:
- 導致死鎖的事務:涉及死鎖的多個事務的線程ID、正在執(zhí)行的SQL語句(可能不是完整的業(yè)務SQL,而是鎖等待相關的內(nèi)部SQL)。
- 事務持有的鎖和等待的鎖:清晰展示了每個事務已經(jīng)持有什么鎖,以及它正在嘗試獲取什么鎖。
- 回滾的事務:InnoDB 選擇哪個事務作為“犧牲品”來回滾以打破死鎖。
- TRANSACTIONS: 當前活躍的事務信息。
- FILE I/O: I/O 相關線程信息。
- BUFFER POOL AND MEMORY: 緩沖池和內(nèi)存的使用統(tǒng)計。
- ROW OPERATIONS: 行操作相關的統(tǒng)計信息。
為什么沒查到你的死鎖信息?
核心原因:**LATEST DETECTED DEADLOCK** 只記錄最近一次死鎖。
想象一下,它是一個只能容納一條記錄的“死鎖日志表”。當發(fā)生一個新的死鎖時,舊的記錄就會被覆蓋。
所以,可能的情況是:
- 在你的程序報錯和你在數(shù)據(jù)庫執(zhí)行
SHOW ENGINE INNODB STATUS;之間的這段時間內(nèi),系統(tǒng)又發(fā)生了新的死鎖,把你遇到的那個死鎖信息給覆蓋掉了。 - 數(shù)據(jù)庫可能在你查詢之前重啟過,因為這份報告是存儲在內(nèi)存中的,重啟后會清空。
方式匯總
方式一:執(zhí)行SHOW ENGINE INNODB STATUS;命令
直接執(zhí)行該命令查看最近的一次死鎖日志記錄:
SHOW ENGINE INNODB STATUS;
方式二:死鎖日志記錄
初始化
步驟一:開啟并配置永久的死鎖日志記錄
這是最重要的一步,能讓你在死鎖發(fā)生后從容地分析。
1、開啟 InnoDB 死鎖日志打印到錯誤日志:
確保你的 MySQL 配置文件中(如 my.cnf 或 my.ini)有以下配置:
[mysqld] innodb_print_all_deadlocks = ON
這個配置會讓 InnoDB 將每一次死鎖的詳細信息都記錄到 MySQL 的錯誤日志(Error Log)中,而不是僅僅在 SHOW ENGINE INNODB STATUS; 中保留最近一次。
修改后需要重啟 MySQL 服務,或者動態(tài)設置(需要有權限):
SET GLOBAL innodb_print_all_deadlocks = ON;
2、配置并監(jiān)控錯誤日志:
找到你的 MySQL 錯誤日志文件路徑(可以通過 SHOW VARIABLES LIKE 'log_error'; 查看)。
之后,每當發(fā)生死鎖,你都可以去這個日志文件里搜索 "DEADLOCK" 關鍵字,找到完整的死鎖報告。
步驟二:捕獲并分析死鎖現(xiàn)場
當你的應用程序(例如,日志中、監(jiān)控系統(tǒng))報告死鎖錯誤時(MySQL 通常會返回 Error 1213),立即去檢查錯誤日志。
你會看到類似這樣的報告(這是一個簡化示例):
*** (1) TRANSACTION: TRANSACTION 123456, ACTIVE 10 sec starting index read mysql tables in use 1, locked 1 LOCK WAIT 4 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1 MySQL thread id 100, OS thread handle 0x..., query id 1000 ... updating DELETE FROM t1 WHERE a = 1 *** (1) HOLDS THE LOCK(S): -- 事務1持有的鎖 RECORD LOCKS space id 100 page no 10 n bits 80 index PRIMARY of table `test`.`t1` trx id 123456 lock_mode X locks rec but not gap Record lock, heap no 5 PHYSICAL RECORD: ... *** (1) WAITING FOR THIS LOCK TO BE GRANTED: -- 事務1在等待的鎖 RECORD LOCKS space id 100 page no 11 n bits 80 index sec_idx of table `test`.`t1` trx id 123456 lock_mode X locks rec but not gap waiting Record lock, heap no 6 PHYSICAL RECORD: ... *** (2) TRANSACTION: TRANSACTION 123457, ACTIVE 15 sec starting index read mysql tables in use 1, locked 1 4 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1 MySQL thread id 101, OS thread handle 0x..., query id 1001 ... updating DELETE FROM t1 WHERE a = 2 *** (2) HOLDS THE LOCK(S): -- 事務2持有的鎖 RECORD LOCKS space id 100 page no 11 n bits 80 index sec_idx of table `test`.`t1` trx id 123457 lock_mode X locks rec but not gap Record lock, heap no 6 PHYSICAL RECORD: ... *** (2) WAITING FOR THIS LOCK TO BE GRANTED: -- 事務2在等待的鎖 RECORD LOCKS space id 100 page no 10 n bits 80 index PRIMARY of table `test`.`t1` trx id 123457 lock_mode X locks rec but not gap waiting Record lock, heap no 5 PHYSICAL RECORD: ... *** WE ROLL BACK TRANSACTION (2) -- 數(shù)據(jù)庫選擇回滾了事務2
步驟三:解讀死鎖報告并定位業(yè)務代碼
分析上面的報告,關鍵是找到:
涉及的事務:兩個(或多個)事務分別在執(zhí)行什么 SQL?
示例中,兩個事務都在執(zhí)行 DELETE,但條件不同 (a=1 和 a=2)。
- 鎖的持有和等待關系:
- 事務1:持有了
PRIMARY索引上某行的X鎖,正在等待sec_idx索引上某行的X鎖。 - 事務2:持有了
sec_idx索引上某行的X鎖,正在等待PRIMARY索引上某行的X鎖。
- 事務1:持有了
- 死鎖成因:
這就形成了一個典型的“循環(huán)等待”:事務1等事務2,事務2又在等事務1。數(shù)據(jù)庫為了打破僵局,選擇回滾其中一個(這里是事務2)。
排查過程
1、立即捕獲死鎖現(xiàn)場
# 1. 立即查詢最新死鎖信息(趁還沒被覆蓋) SHOW ENGINE INNODB STATUS\G # 2. 檢查死鎖記錄是否已開啟 SHOW VARIABLES LIKE 'innodb_print_all_deadlocks'; # 3. 如果未開啟,立即開啟(避免后續(xù)死鎖丟失) SET GLOBAL innodb_print_all_deadlocks = ON;
2、定位日志中的死鎖情況
# 1. 找到錯誤日志位置 mysql -e "SHOW VARIABLES LIKE 'log_error';" # 2. 實時監(jiān)控錯誤日志中的死鎖(推薦) tail -f /var/log/mysql/error.log | grep -A 50 -B 5 "DEADLOCK" # 3. 或者搜索歷史死鎖記錄 grep -A 50 "DEADLOCK" /var/log/mysql/error.log
3、快速分析核心問題
在死鎖日志中,重點關注以下4個部分: text LATEST DETECTED DEADLOCK ------------------------ *** (1) TRANSACTION: [關鍵點1:事務1信息] TRANSACTION 1234, ACTIVE 10 sec updating mysql tables in use 1, locked 1 [看這里→] UPDATE table_x SET ... WHERE id = 1 [關鍵SQL1] *** (1) HOLDS THE LOCK(S): [關鍵點2:事務1持有的鎖] RECORD LOCKS index `idx_name` of table `db`.`table` trx id 1234 lock_mode X *** (1) WAITING FOR THIS LOCK(S): [關鍵點3:事務1等待的鎖] RECORD LOCKS index `primary` of table `db`.`table` trx id 1234 lock_mode X waiting *** (2) TRANSACTION: [關鍵點4:事務2信息] TRANSACTION 1235, ACTIVE 8 sec updating [看這里→] UPDATE table_x SET ... WHERE id = 2 [關鍵SQL2]
4、快速診斷模板
對照這個模板分析:
| 檢查項 | 要問的問題 | 常見原因 |
|---|---|---|
| 死鎖類型 | 是不同索引沖突還是相同資源爭用? | 跨索引死鎖常見 |
| SQL語句 | 兩個事務執(zhí)行的具體SQL是什么? | UPDATE/DELETE 容易死鎖 |
| 資源順序 | 事務訪問資源的順序是否一致? | 順序不一致是主因 |
| 事務大小 | 事務中是否包含多個SQL? | 大事務容易死鎖 |
| 索引使用 | WHERE條件是否走了合適索引? | 無索引導致鎖表 |
緊急應對策略:
-- 1. 查看當前鎖等待情況(輔助分析) SELECT * FROM information_schema.INNODB_LOCKS; SELECT * FROM information_schema.INNODB_LOCK_WAITS; -- 2. 查看當前活躍事務 SELECT * FROM information_schema.INNODB_TRX ORDER BY trx_started DESC;
死鎖解決方案
1、分析是否事務過大導致。
2、有無涉及到兩段代碼為:
A B
B A
的過程
相關排查思路文章
[2]. MySQL 故障案例分析:從死鎖到數(shù)據(jù)丟失的全面診斷指南
到此這篇關于MySQL死鎖類deadlock問題排查的文章就介紹到這了,更多相關MySQL死鎖類deadlock排查內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
MySQL?驅(qū)動中虛引用?GC?耗時優(yōu)化與源碼分析
這篇文章主要為大家介紹了MySQL?驅(qū)動中虛引用?GC?耗時優(yōu)化與源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-05-05
mysql如何獲取數(shù)據(jù)列值(int和string)最大值
最近在開發(fā)項目的時候有個需求,我數(shù)據(jù)庫里面存了很多升級包,升級包有列數(shù)據(jù)表示的是升級包的版本號,類型屬于字符串,結構類似于V1.0.2.22這種,然后后臺有個任務需要獲取最新版本號的那條數(shù)據(jù),本文給大家介紹mysql獲取數(shù)據(jù)列值(int和string)最大值,感興趣的朋友一起看看吧2024-01-01
mysql 單機數(shù)據(jù)庫優(yōu)化的一些實踐
這篇文章主要介紹了mysql 單機數(shù)據(jù)庫優(yōu)化的一些實踐的相關資料,需要的朋友可以參考下2016-09-09
MySQL單表百萬數(shù)據(jù)記錄分頁性能優(yōu)化技巧
自己的一個網(wǎng)站,由于單表的數(shù)據(jù)記錄高達了一百萬條,造成數(shù)據(jù)訪問很慢,Google分析的后臺經(jīng)常報告超時,尤其是頁碼大的頁面更是慢的不行2016-08-08
MySQL查詢?nèi)罩綠eneral Log的配置與作用
本文將詳細介紹 General Log的配置方法、查看方式、核心作用及使用注意事項,幫助運維和開發(fā)人員高效利用該日志工具,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2026-04-04
MySql,MVCC實現(xiàn)及其機制,快照讀在RC,RR下的區(qū)別說明
這篇文章主要介紹了MySql,MVCC實現(xiàn)及其機制,快照讀在RC,RR下的區(qū)別說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-04-04

