一次Mariadb死鎖排查過程回顧

場景

在使用某個平臺的時候,有些頁面發現長時間,部分數據無法加載成功,開始排查問題。

確定是mariadb的問題的過程

訪問了幾個頁面都是正常的,唯獨某幾個頁面查詢實時監控數據時無法加載出來,F12查看接口發現有幾個業務相似的接口長時間不返回數據。

既然整體功能是正常的,只有部分頁面出現問題,而且都是實時數據無法顯示,懷疑是同一個地方出現問題,於是把接口放在一起發現共同點。

  1. 都是timeout長時間無響應,而且不是前端資源加載的問題,F12可以看到一個接口的請求過程,例如下圖

  1. 查看代碼邏輯 ,發現幾個接口同時查詢過一個表,登陸mariadb,發現SELECT長時間不返回。懷疑是鎖表了。

以下所有示例使用docker啓動mysql演示覆現。

我先鎖表

lock table test read;
lock table test write;

發現是卡住了

mysql> select * from test;
mysql> 2013 - Lost connection to MySQL server during query

查看長時間卡住的線程

查詢進程(如果您有SUPER權限,您可以看到所有線程。否則,您只能看到您自己的線程)

show processlist;

但是此命令只能顯示前100條數據,要想看全部的數據,請輸入

show full processlist;

這個命令中最關鍵的就是state列,mysql列出的狀態主要有這幾種,點擊此處展開查看

Checking table 正在檢查數據表(這是自動的)。Closing tables 正在將表中修改的數據刷新到磁盤中,同時正在關閉已經用完的表。這是一個很快的操作,如果不是這樣的話,就應該確認磁盤空間是否已經滿了或者磁盤是否正處於重負中。Connect Out 複製從服務器正在連接主服務器。Copying to tmp table on disk 由於臨時結果集大於tmptablesize,正在將臨時表從內存存儲轉爲磁盤存儲以此節省內存。Creating tmp table 正在創建臨時表以存放部分查詢結果。deleting from main table 服務器正在執行多表刪除中的第一部分,剛刪除第一個表。deleting from reference tables 服務器正在執行多表刪除中的第二部分,正在刪除其他表的記錄。Flushing tables 正在執行FLUSH TABLES,等待其他線程關閉數據表。Killed 發送了一個kill請求給某線程,那麼這個線程將會檢查kill標誌位,同時會放棄下一個kill請求。MySQL會在每次的主循環中檢查kill標誌位,不過有些情況下該線程可能會過一小段才能死掉。如果該線程程被其他線程鎖住了,那麼kill請求會在鎖釋放時馬上生效。Locked 被其他查詢鎖住了。Sending data 正在處理SELECT查詢的記錄,同時正在把結果發送給客戶端。Sorting for group 正在爲GROUP BY做排序。 Sorting for order 正在爲ORDER BY做排序。Opening tables 這個過程應該會很快,除非受到其他因素的干擾。例如,在執ALTER TABLE或LOCK TABLE語句行完以前,數據表無法被其他線程打開。正嘗試打開一個表。Removing duplicates 正在執行一個SELECT DISTINCT方式的查詢,但是MySQL無法在前一個階段優化掉那些重複的記錄。因此,MySQL需要再次去掉重複的記錄,然後再把結果發送給客戶端。Reopen table 獲得了對一個表的鎖,但是必須在表結構修改之後才能獲得這個鎖。已經釋放鎖,關閉數據表,正嘗試重新打開數據表。Repair by sorting 修復指令正在排序以創建索引。Repair with keycache 修復指令正在利用索引緩存一個一個地創建新索引。它會比Repair by sorting慢些。Searching rows for update 正在講符合條件的記錄找出來以備更新。它必須在UPDATE要修改相關的記錄之前就完成了。Sleeping 正在等待客戶端發送新請求.System lock 正在等待取得一個外部的系統鎖。如果當前沒有運行多個mysqld服務器同時請求同一個表,那麼可以通過增加--skip-external-locking參數來禁止外部系統鎖。Upgrading lock INSERT DELAYED正在嘗試取得一個鎖表以插入新記錄。Updating 正在搜索匹配的記錄,並且修改它們。User Lock 正在等待GET_LOCK()。Waiting for tables 該線程得到通知,數據表結構已經被修改了,需要重新打開數據表以取得新的結構。然後,爲了能的重新打開數據表,必須等到所有其他線程關閉這個表。以下幾種情況下會產生這個通知:FLUSH TABLES tbl_name, ALTER TABLE, RENAME TABLE, REPAIR TABLE, ANALYZE TABLE,或OPTIMIZE TABLE。waiting for handler insert INSERT DELAYED已經處理完了所有待處理的插入操作,正在等待新的請求。 大部分狀態對應很快的操作,只要有一個線程保持同一個狀態好幾秒鐘,那麼可能是有問題發生了,需要檢查一下。 還有其他的狀態沒在上面中列出來,不過它們大部分只是在查看服務器是否有存在錯誤是才用得着。

假如發現鎖表進程,請輸入kill指令將他剔除。

mysql> kill 9;
Query OK, 0 rows affected (0.00 sec)
mysql> kill 11;
Query OK, 0 rows affected (0.00 sec)

再看已經沒有了

也能查詢了。

mysql> select * from test;
+----+------+
| id | a    |
+----+------+
|  1 | test |
|  2 | a    |
+----+------+
2 rows in set (0.00 sec)

查詢是否鎖表

但是剛剛我加的鎖並沒有看到是哪個表被鎖,也沒看到狀態,能修復純屬瞎貓碰上死耗子,而且線上生產環境最忌諱就是不知道線程是幹嘛的隨便殺了,一個有經驗的運維是不會這麼做的 。

我重新把表鎖上。用這個命令查看打開了哪些表 (不包括臨時表)。

mysql> show OPEN TABLES where In_use > 0;
+------------+-------+--------+-------------+
| Database   | Table | In_use | Name_locked |
+------------+-------+--------+-------------+
| coding3min | test  |      1 |           0 |
+------------+-------+--------+-------------+
1 row in set (0.00 sec)

In_use 表的表鎖或鎖請求數(已經鎖表,或等待鎖表)

Name_locked 顯示錶名稱是否被鎖定(DDL)

mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)
 
mysql> show OPEN TABLES where In_use > 0;
Empty set

查看事物鎖

事物鎖不同於表鎖,

select trx_tables_locked,trx_tables_in_use,
       trx_rows_locked,trx_mysql_thread_id,trx_state
from information_schema.innodb_trx;

手動創建一個更新的事物

mysql> start transaction;
Query OK, 0 rows affected (0.01 sec)

mysql> update test2 set a='111';
Query OK, 2 rows affected (0.00 sec)
Rows matched: 2  Changed: 2  Warnings: 0

只要trx_tables_locked,trx_tables_in_use,trx_rows_locked三個字段其中一個有值,說明此記錄是事物鎖,try_mysql_thread_id 就是這個線程idkill掉,即可。

我另開一個終端,再啓動一個事物,因爲前一個事物沒有停止,所以會產生一個鎖等待。

mysql> use coding3min;
Database changed
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> update test2 set a='222'; 
1205 - Lock wait timeout exceeded; try restarting transaction

再查詢一次

mysql> kill 20;
1317 - Query execution was interrupted
mysql> kill 21;
Query OK, 0 rows affected (0.01 sec)

mysql> select trx_tables_locked,trx_tables_in_use,
trx_rows_locked,trx_mysql_thread_id,trx_state
from information_schema.innodb_trx;
Empty set

妥了,可能kill會等待好幾分鐘,耐心等待。

最後的話

每種操作都會在不同的策略確保kill操作的線程消亡掉。

mysql kill處理的機制看,在mysql hang住的情況下,大量寫操作被阻塞,使用kill並不能立即解決問題,如果想盡快讓mysql恢復服務,最快的是主備切換,或直接重啓mysql。

最重要的還是搞清楚所有的死鎖情況,優化代碼,不再出現類似的問題。

引用

mysql查看死鎖和解除鎖mysql鎖表解決方案

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