MySQL-鎖等待排查

背景

最近我們的登錄系統在每個鐘點的 18分就會登錄不進去, 排查後發現有鎖等待情況 , 週期地發生那麼很大機率是自動任務了, 爲了找到爲什麼會鎖等待 ,我們做了以下的排查

排查過程

-- 1.鎖住的事務
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;

-- 2.事務
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;

-- 3.等待的鎖
SELECT * FROM information_schema.INNODB_LOCK_waits;

-- 4.當前進行的進程
SHOW FULL PROCESSLIST;

先看 1.鎖住的事務 3.等待的鎖


-- 1.鎖住的事務
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
+-----------------+-----------+---------+---------+--------------------+----------+----------+---------+--------+---------+
|lock_id          |lock_trx_id|lock_mode|lock_type|lock_table          |lock_index|lock_space|lock_page|lock_rec|lock_data|
+-----------------+-----------+---------+---------+--------------------+----------+----------+---------+--------+---------+
|45982383:101:7:72|45982383   |X        |RECORD   |`mdp`.`mdp_sec_user`|PRIMARY   |101       |7        |72      |1        |
|45982369:101:7:72|45982369   |S        |RECORD   |`mdp`.`mdp_sec_user`|PRIMARY   |101       |7        |72      |1        |
+-----------------+-----------+---------+---------+--------------------+----------+----------+---------+--------+---------+
           


-- 3.等待的鎖
SELECT * FROM information_schema.INNODB_LOCK_waits;
+-----------------+-----------------+---------------+-----------------+
|requesting_trx_id|requested_lock_id|blocking_trx_id|blocking_lock_id |
+-----------------+-----------------+---------------+-----------------+
|45982383         |45982383:101:7:72|45982369       |45982369:101:7:72|
+-----------------+-----------------+---------------+-----------------+

圖片比較好看點 :

img

可以看到 , lock_trx_id 爲 45982369 , 持有的是 S鎖-讀鎖 , 而 45982383 持有的是 X鎖-寫鎖 , 我們知道這是衝突的, 並且從3.等待的鎖可以看到 45982383 正在等待 45982369 釋放鎖 . 我們看一下這個 45982383 和 45982369 各是執行了什麼 SQL , 導致讀寫鎖阻塞了.

然後接着看 2.事務 4.當前進行的進程

img


img

然後我們把 SQL 複製出來

持有 S 鎖 的 SQL :

INSERT INTO dataauth.ccs_data_auth_user_org (
            USER_ID,
            ORG_ID,
            ORG_CODE,
            ORG_NAME,
            SETS_OF_BOOKS_ID,
            CREATION_DATE,
            LAST_UPDATE_DATE
        ) SELECT
            u.USER_ID,
            cus.CORG_ID,
            cus.CORG_CODE,
            cus.CORG_NAME,
            u.SETS_OF_BOOKS_ID,
            now(),
            now()
        FROM
            mdp.mdp_sec_user u
        INNER JOIN mdp.mdp_sec_org_staff s ON u.USER_ID = s.USER_ID
        INNER JOIN mdp.mdp_sec_org o ON s.ORG_ID = o.ORG_ID
        INNER JOIN (
            SELECT
                c.CORG_ID,
                c.CORG_CODE,
                c.CORG_NAME,
                cl.CORG_CODE CORG_LINE_CODE,
                c.SETS_OF_BOOKS_ID
            FROM
                basedata.ccs_base_customer c,
                basedata.ccs_base_customer_line cl
            WHERE
                c.CUSTOMER_ID = cl.CUSTOMER_ID
            AND c.STORE_TYPE IN (1, 2)
            AND c.CORG_CODE IS NOT NULL
        ) cus
        WHERE
            cus.CORG_LINE_CODE LIKE concat(o.`CODE`, '%')
        AND u.SETS_OF_BOOKS_ID = cus.SETS_OF_BOOKS_ID
        ON DUPLICATE KEY UPDATE
           LAST_UPDATE_DATE = VALUES(LAST_UPDATE_DATE)

持有 X 鎖 的 SQL :

update mdp.mdp_sec_user u set LAST_UPDATE_DATE = 'xx' where LOGIN_ID = 'YY' ;  

剩下的就是 SQL 優化了

SQL 字段說明

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
  • lock_id:鎖定的標識符。
  • lock_trx_id:持有鎖定的事務 ID。
  • lock_mode:鎖定的模式,如 SHARED、EXCLUSIVE、RECORD、TABLE 等。
  • lock_type:鎖定的類型,如 RECORD、TABLE、PAGE 等。
  • lock_table:鎖定的表名。
  • lock_index:鎖定的索引名。
  • lock_space:鎖定的表空間 ID。
  • lock_page:鎖定的頁碼。
    需要注意的是,查詢 INFORMATION_SCHEMA.INNODB_LOCKS 表需要具有相應的權限。如果您沒有足夠的權限,可能無法執行該查詢語句。

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
 SELECT * FROM information_schema.INNODB_LOCK_waits 
  • requesting_trx_id:正在等待鎖定的事務 ID。
  • requested_lock_id:正在等待的鎖定 ID。
  • blocking_trx_id:正在持有鎖定的事務 ID。
  • blocking_lock_id:正在持有的鎖定 ID。
  • requested_lock_type:正在等待的鎖定類型,如 S、X、IS、IX 等。
  • blocking_lock_type:正在持有的鎖定類型,如 S、X、IS、IX 等。
  • requested_table:正在等待鎖定的表名。
  • blocking_table:正在持有鎖定的表名。
  • requested_index:正在等待鎖定的索引名。
  • blocking_index:正在持有鎖定的索引名。

殺死阻塞進程

有時候在生產環境 ,我們不得不先殺死阻塞的線程 ,以便不阻塞生產業務.

#!/bin/bash
mysql -u root -e "show processlist" | grep -i "Locked" >> locked_log.txt

for line in `cat locked_log.txt | awk '{print $1}'`
do 
   echo "kill $line;" >> kill_thread_id.sql
done

現在kill_thread_id.sql的內容像這個樣子

kill 66402982;
kill 66402983;
kill 66402986;
kill 66402991;

好了, 我們在mysql的shell中執行, 就可以把所有鎖表的進程殺死了.執行上面的SQL

mysql>source kill_thread_id.sql

參考資料

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