[數據庫] 排查MySQL鎖表情況及解決思路

0 序

1 排查方法:查看當前鎖表事務

由於出現的是鎖表的問題,所以第一步從數據庫入手,查看導致鎖表的SQL語句是什麼;查看是否鎖表SQL語句;

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;

該命令的輸出結果包含了當前事務等待的鎖資源的相關信息,包括事務ID、鎖的類型、鎖的模式、被鎖定的對象(表、頁等)以及鎖的狀態,而且也能看到具體執行的SQL以及事務的權重,反應一個事務修改和鎖定的行數。

2 排查方法(擴展)

除了上面查詢方式,MySQL還提供了很多查看方式,來查看錶是否被鎖定。以下是常用的幾種方式:

方法一:使用SHOW OPEN TABLES命令

SHOW OPEN TABLES WHERE `Table` = 'table_name' AND `Database` = 'database_name';

這個命令會返回一個結果集,其中包含了表的一些信息,比如表的狀態,使用的存儲引擎等等。如果表被鎖定,那麼狀態字段會顯示In_use。

方法二:使用SHOW PROCESSLIST命令

SHOW PROCESSLIST;

這個命令會返回當前MySQL服務器上所有的活動進程。如果表被鎖定,那麼可以通過查看這個進程列表來確定是否有進程正在使用該表。你可以檢查State列中的信息,看是否有進程正在鎖定該表。

方法三:使用INFORMATION_SCHEMA系統庫

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS WHERE `table_name` = 'table_name';

這個查詢語句會返回InnoDB引擎的鎖信息。如果表被鎖定,你可以在結果集中找到相關的行。

方法四:使用SHOW ENGINE INNODB STATUS命令

SHOW ENGINE INNODB STATUS

這個命令會返回InnoDB引擎的狀態信息。你可以在結果中查找TRANSACTIONS和LOCK WAIT字段來確定是否有事務正在等待表鎖定。

方法五:使用sys.innodb_lock_waits系統視圖(僅適用於MySQL 8.0及以上版本)

SELECT * FROM sys.innodb_lock_waits;

這個查詢會返回當前等待鎖定的事務信息。如果表被鎖定,你可以在結果集中找到相關的行。

方法六:InnoDB表引擎的鎖狀態

SHOW STATUS LIKE '%innodb_row_lock%'

重要指標:等待平均時長、等待總時長、等待總次數

3 解決思路:治本之道

以上提供了一些查看問題的方式,那麼既然出現了問題,就需要徹底根治,避免系統再次出現問題,針對系統本次出現的問題,從一下幾個點進行了優化,具體如下:

(1) SQL 本身優化

  • 對跑批SQL進行了執行計劃分析,通過分析查看,發現有些關聯表進行了全表查詢,所以第一步先多查詢速度進行優化,從查詢時間上入手解決,通過多次執行計劃分析,對進行了全表掃描的做關聯關係分析,發現部分表有主鍵,但爲未建索引;

因此從SQL做了以下優化:

  • 1、建索引
CREATE INDEX index_name ON table_name (column_name);
  • 2、減少子查詢
  • 3、添加where條件
  • 4、查詢條件避免使用函數、模糊搜索等查詢效率較慢的查詢方式

(2) 編碼層面

由於處理的數據量比較多,數據來源比較複雜,來源多個表,所以將有些能抽出來的表抽出來,儘量放代碼層面處理,通過代碼邏輯控制;

本次優化只是從這幾個方面優化,想有優化sql還是需要從sql的本身進行分析,知道執行順序以及原理,執行原理可見如下文章:

一條SQL語句從開始到結束到底經歷了什麼? - CSDN

4 解決方法:鎖的釋放(臨時解決)

本次問題從sql,系統層面解決了,那麼爲能臨時解決鎖表,保證系統正常運行,先對導致鎖表的事務進行釋放,MySQL中鎖的釋放是自動進行的,當一個會話執行完相關操作後,所持有的鎖會自動釋放。不過,有些情況下我們可能需要手動釋放鎖,比如長事務或者死鎖的處理。釋放鎖SQL語句如下:

A、ROLLBACK

當一個會話執行ROLLBACK語句時,所有該會話持有的鎖都會被立即釋放。例如:

START TRANSACTION;
SELECT * FROM table1 WHERE id = 1 FOR UPDATE;
ROLLBACK;

在這個例子中,執行ROLLBACK後,會話所持有的鎖會被釋放。

B、COMMIT

當一個會話執行COMMIT語句時,所有該會話持有的鎖都會被釋放。例如:

START TRANSACTION;
SELECT * FROM table1 WHERE id = 1 FOR UPDATE;
COMMIT;

在這個例子中,執行COMMIT後,會話所持有的鎖會被釋放。

C、顯式調用UNLOCK TABLES

當一個會話調用UNLOCK TABLES語句時,會釋放該會話持有的所有表級鎖。例如:

寫鎖

LOCK TABLES table1 WRITE;
...
UNLOCK TABLES;

讀鎖

LOCK TABLES table1 READ;
...
UNLOCK TABLES;

在這個例子中,調用UNLOCK TABLES後,會話所持有的鎖會被釋放。

D、長事務的處理

在MySQL中,長事務是指持續運行時間較長的事務。長事務可能導致鎖保持的時間較長,從而影響其他會話的併發訪問能力。爲了釋放長事務持有的鎖,可以使用以下方法:

  • 執行ROLLBACK或者COMMIT語句來結束事務。
  • 使用KILL命令終止會話,但這種方法可能會導致事務的回滾。

E、死鎖的處理

當多個會話之間出現循環依賴的鎖競爭關係時,就會發生死鎖。MySQL會自動檢測到死鎖,並選擇一個會話進行回滾,以解除死鎖。
使用SHOW ENGINE INNODB STATUS命令可以查看死鎖信息。例如:

SHOW ENGINE INNODB STATUS

在輸出結果中的"TRANSACTIONS"部分,可以找到死鎖的詳細信息;
通過以上方式,從系統本身的編碼,SQL語句,數據庫表關鍵字做優化之後,鎖表情況也徹底解決;

Y FAQ

Q: MySQL問題之基於Java Hibernate ORM框架,當數據庫數據源被鎖(Table Metadata Lock)時的解決方案

注:原文發於: [數據庫] MySQL問題之當數據庫數據源被鎖(Table Metadata Lock)時的解決方案 - 博客園/千千寰宇 | 2017-11-05 18:55 【原文已刪,歸檔至此】

  1. 發生的原因分析:【hibernate的線程池連接導致了不能修改被鎖定數據庫的數據庫型模式】
  2. 關掉hibernate的所有線程池(選擇退出IDE或者其他辦法)
  3. 查看被鎖的進程ID:show full processlist
  4. 殺掉該進程ID:kill + ID
  5. 提交導致數據源被鎖的事務:COMMIT 或者 ROLLBACK

X 參考文獻

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