MySQL資料庫 DDL 阻塞問題定位 【轉載】

来源:https://www.cnblogs.com/xuliuzai/archive/2022/06/25/16411582.html
-Advertisement-
Play Games

1、簡述 binlog 二進位日誌文件,這個文件記錄了MySQL所有的DML操作。通過binlog日誌我們可以做數據恢復,增量備份,主主複製和主從複製等等。 2、Docker中無法使用vim問題解決 https://blog.csdn.net/Tomwildboar/article/details/ ...


轉載

【即拿即用:MySQL 中如何定位 DDL 被阻塞的問題?】

https://dbaplus.cn/news-11-4579-1.html

作者介紹

陳臣,甲骨文MySQL首席解決方案工程師,公眾號《MySQL實戰》作者,有大規模的MySQL,Redis,MongoDB,ES的管理和維護經驗,擅長MySQL資料庫的性能優化及日常操作的原理剖析。

1.引入

經常碰到開發、測試童鞋會問,線下開發、測試環境,執行了一個DDL,發現很久都沒有執行完,是不是被阻塞了?要怎麼解決?包括在群里,也經常會碰到類似問題:DDL 被阻塞了,如何找到阻塞它的 SQL ?

實際上,如何解決 DDL 被阻塞的問題,是 MySQL 中一個共性且高頻的問題。

下麵,就這個問題,給一個清晰明瞭、拿來即用的解決方案:

  • 怎麼判斷一個DDL是不是被阻塞了 ?

  • 當DDL被阻塞時,怎麼找出阻塞它的會話 ?

2.怎麼判斷一個 DDL是不是被阻塞了

首先,看一個簡單的Demo。

session1> create table sbtest.t1(id int primary key,name varchar(10));
Query OK, 0 rows affected (0.02 sec)
session1> insert into sbtest.t1 values(1,'a');
Query OK, 1 row affected (0.01 sec)
session1> begin;
Query OK, 0 rows affected (0.00 sec)
session1> select * from sbtest.t1;
+----+------+
| id | name |
+----+------+
|  1 | a    |
+----+------+
1 row in set (0.00 sec)
session2> alter table sbtest.t1 add c1 datetime;
阻塞中。。。
session3> show processlist;
+----+-----------------+-----------+------+---------+-------+---------------------------------+---------------------------------------+
| Id | User            | Host      | db   | Command | Time  | State                           | Info                                  |
+----+-----------------+-----------+------+---------+-------+---------------------------------+---------------------------------------+
|  5 | event_scheduler | localhost | NULL | Daemon  | 47628 | Waiting on empty queue          | NULL                                  |
| 24 | root            | localhost | NULL | Sleep   |    11 |                                 | NULL                                  |
| 25 | root            | localhost | NULL | Query   |     5 | Waiting for table metadata lock | alter table sbtest.t1 add c1 datetime |
| 26 | root            | localhost | NULL | Query   |     0 | init                            | show processlist                      |
+----+-----------------+-----------+------+---------+-------+---------------------------------+---------------------------------------+
4 rows in set (0.00 sec)

判斷一個 DDL 是不是被阻塞了,很簡單,就是執行 show processlist ,查看 DDL 操作對應的狀態。如果顯示的是 Waiting for table metadata lock ,則意味著這個 DDL 被阻塞了。DDL 一旦被阻塞了,後續針對該表的所有操作都會被阻塞,都會顯示 Waiting for table metadata lock 。這也是 DDL 讓人聞之色變的原因。碰到了類似場景,要麼 Kill DDL 操作,要麼 Kill 阻塞 DDL 的會話。Kill DDL 操作是一個治標不治本的方法,畢竟 DDL 操作總要執行。除此之外,對於 DDL 操作,需要獲取元資料庫鎖的階段有兩個:DDL 開始之初和 DDL 結束之前。如果是後者,就意味著之前的操作都要回滾,成本相對較高。所以,碰到類似場景,我們一般都會 Kill 阻塞 DDL 的會話。

那麼,怎麼知道是哪些會話阻塞了 DDL 呢?下麵我們看看具體的定位方法。

3.定位方法

3.1 方法一:sys.schema_table_lock_waits

sys.schema_table_lock_waits 是MySQL 5.7引入的,用來定位 DDL 被阻塞的問題。

針對上面這個Demo,我們看看sys.schema_table_lock_waits的輸出。

mysql> select * from sys.schema_table_lock_waits\G
*************************** 1. row ***************************
               object_schema: sbtest
                 object_name: t1
           waiting_thread_id: 62
                 waiting_pid: 25
             waiting_account: root@localhost
           waiting_lock_type: EXCLUSIVE
       waiting_lock_duration: TRANSACTION
               waiting_query: alter table sbtest.t1 add c1 datetime
          waiting_query_secs: 17
 waiting_query_rows_affected: 0
 waiting_query_rows_examined: 0
          blocking_thread_id: 61
                blocking_pid: 24
            blocking_account: root@localhost
          blocking_lock_type: SHARED_READ
      blocking_lock_duration: TRANSACTION
     sql_kill_blocking_query: KILL QUERY 24
sql_kill_blocking_connection: KILL 24
*************************** 2. row ***************************
               object_schema: sbtest
                 object_name: t1
           waiting_thread_id: 62
                 waiting_pid: 25
             waiting_account: root@localhost
           waiting_lock_type: EXCLUSIVE
       waiting_lock_duration: TRANSACTION
               waiting_query: alter table sbtest.t1 add c1 datetime
          waiting_query_secs: 17
 waiting_query_rows_affected: 0
 waiting_query_rows_examined: 0
          blocking_thread_id: 62
                blocking_pid: 25
            blocking_account: root@localhost
          blocking_lock_type: SHARED_UPGRADABLE
      blocking_lock_duration: TRANSACTION
     sql_kill_blocking_query: KILL QUERY 25
sql_kill_blocking_connection: KILL 25
2 rows in set (0.00 sec)

只有一個 alter 操作,卻產生了兩條記錄,而且兩條記錄的 Kill 對象還不一樣,其中一條 Kill 的對象還是 alter 操作本身。如果對錶結構不熟悉或不仔細看記錄內容的話,難免會 Kill 錯對象。不僅如此,在 DDL 操作被阻塞後,如果後續有 N 個查詢被 DDL 操作堵塞,還會產生 N*2 條記錄。在定位問題時,這 N*2 條記錄完全是個噪音。這個時候,就需要我們對上述記錄進行過濾了。過濾的關鍵是 blocking_lock_type 不等於 SHARED_UPGRADABLE。SHARED_UPGRADABLE 是一個可升級的共用元數據鎖,加鎖期間,允許併發查詢和更新,常用在 DDL 操作的第一階段。所以,阻塞DDL的不會是SHARED_UPGRADABLE。

故而,針對上面這個 case,我們可以通過下麵這個查詢來精確地定位出需要 Kill 的會話。

SELECT sql_kill_blocking_connection
FROM sys.schema_table_lock_waits
WHERE blocking_lock_type <> 'SHARED_UPGRADABLE'
 AND waiting_query = 'alter table sbtest.t1 add c1 datetime';

 3.2 方法二:Kill DDL 之前的會話

sys.schema_table_lock_waits 是 MySQL 5.7 才引入的。但在實際生產環境,MySQL 5.6還是占有相當多的份額。如何解決MySQL 5.6的這個痛點呢 ?細究下來,導致 DDL 被阻塞的操作,無非兩類:

  • 表上有慢查詢未結束。

  • 表上有事務未提交。

其中,第一類比較好定位,通過 show processlist 就能發現。而第二類僅憑 show processlist 很難定位,因為未提交事務的連接在 show processlist 中的狀態同空閑連接一樣,都是 Sleep 。所以,網上有 Kill 空閑連接的說法,其實也不無道理,但這樣做就太簡單粗暴了,難免會誤殺。其實,既然是事務,在 information_schema.innodb_trx中肯定會有記錄,如 session1 中的事務,在表中的記錄如下,

mysql> select * from information_schema.innodb_trx\G
*************************** 1. row ***************************
                    trx_id: 421568246406360
                 trx_state: RUNNING
               trx_started: 2022-01-02 08:53:50
     trx_requested_lock_id: NULL
          trx_wait_started: NULL
                trx_weight: 0
       trx_mysql_thread_id: 24
                 trx_query: NULL
       trx_operation_state: NULL
         trx_tables_in_use: 0
         trx_tables_locked: 0
          trx_lock_structs: 0
     trx_lock_memory_bytes: 1128
           trx_rows_locked: 0
         trx_rows_modified: 0
   trx_concurrency_tickets: 0
       trx_isolation_level: REPEATABLE READ
         trx_unique_checks: 1
    trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_adaptive_hash_latched: 0
 trx_adaptive_hash_timeout: 0
          trx_is_read_only: 0
trx_autocommit_non_locking: 0
       trx_schedule_weight: NULL
1 row in set (0.00 sec)

其中 trx_mysql_thread_id 是線程 id ,結合 information_schema.processlist ,可進一步縮小範圍。所以,我們可以通過下麵這個 SQL ,定位出執行時間早於 DDL 的事務。

SELECT concat('kill ', i.trx_mysql_thread_id, ';')
FROM information_schema.innodb_trx i, (
    SELECT MAX(time) AS max_time
    FROM information_schema.processlist
    WHERE state = 'Waiting for table metadata lock'
      AND (info LIKE 'alter%'
      OR info LIKE 'create%'
      OR info LIKE 'drop%'
      OR info LIKE 'truncate%'
      OR info LIKE 'rename%'
  )) p
WHERE timestampdiff(second, i.trx_started, now()) > p.max_time;

可喜的是,當前正在執行的查詢也會顯示在information_schema.innodb_trx中。所以,上面這個 SQL 同樣也適用於慢查詢未結束的場景。

4.MySQL 5.7中使用sys.schema_table_lock_waits的註意事項

sys.schema_table_lock_waits 視圖依賴了一張 MDL 相關的表-performance_schema.metadata_locks。該表是 MySQL 5.7 引入的,會顯示 MDL 的相關信息,包括作用對象、鎖的類型及鎖的狀態等。但在 MySQL 5.7 中,該表預設為空,因為與之相關的 instrument 預設沒有開啟。MySQL 8.0 才預設開啟。

mysql> select * from performance_schema.setup_instruments where name='wait/lock/metadata/sql/mdl';
+----------------------------+---------+-------+
| NAME                       | ENABLED | TIMED |
+----------------------------+---------+-------+
| wait/lock/metadata/sql/mdl | NO      | NO    |
+----------------------------+---------+-------+
1 row in set (0.00 sec)

所以,在 MySQL 5.7 中,如果我們要使用 sys.schema_table_lock_waits ,必須首先開啟 MDL 相關的 instrument。開啟方式很簡單,直接修改 performance_schema.setup_instruments 表即可。

具體SQL如下:

UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES'
WHERE NAME = 'wait/lock/metadata/sql/mdl';

但這種方式是臨時生效,實例重啟後,又會恢復為預設值。建議同步修改配置文件。

[mysqld]
performance-schema-instrument='wait/lock/metadata/sql/mdl=ON'

5.總結

1)執行 show processlist ,如果 DDL 的狀態是 Waiting for table metadata lock  ,則意味著這個 DDL 被阻塞了。

2)定位導致 DDL 被阻塞的會話,常用的方法有兩種:

  • sys.schema_table_lock_waits

SELECT sql_kill_blocking_connection
FROM sys.schema_table_lock_waits
WHERE blocking_lock_type <> 'SHARED_UPGRADABLE'
  AND (waiting_query LIKE 'alter%'
  OR waiting_query LIKE 'create%'
  OR waiting_query LIKE 'drop%'
  OR waiting_query LIKE 'truncate%'
  OR waiting_query LIKE 'rename%');

這種方法適用於 MySQL 5.7 和 8.0。

註意,MySQL 5.7 中,MDL 相關的 instrument 預設沒有打開。

  • Kill DDL 之前的會話
SELECT concat('kill ', i.trx_mysql_thread_id, ';')
FROM information_schema.innodb_trx i, (
    SELECT MAX(time) AS max_time
    FROM information_schema.processlist
    WHERE state = 'Waiting for table metadata lock'
      AND (info LIKE 'alter%'
      OR info LIKE 'create%'
      OR info LIKE 'drop%'
      OR info LIKE 'truncate%'
      OR info LIKE 'rename%'
  )) p
WHERE timestampdiff(second, i.trx_started, now()) > p.max_time;

如果 MySQL 5.7 中 MDL 相關的 instrument 沒有打開或在 MySQL 5.6 中,可使用該方法。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 表弟大學快畢業了,學了一個學期Python居然還不會寫學生管理系統,真的給我丟臉啊,教他又不肯學,還讓我直接給他寫,我真想兩巴掌上去,最終還是寫了給他,誰讓他是我表弟呢,關鍵時候還是得幫他一把! 寫完了放在那也是放著,所以今天分享給大家吧! 話不多說,咱們直接開始吧! 代碼解析 一、登錄頁面 1、定 ...
  • 領域邏輯 & 應用邏輯 如前所述,領域驅動設計中的業務邏輯分為兩部分(層):領域邏輯和應用邏輯: 領域邏輯由系統的核心領域規則組成,應用邏輯實現應用特定的用例 雖然定義很明確,但實現起來可能並不容易。您可能無法決定哪些代碼應該位於應用程式層,哪些代碼應該位於領域層。本節試圖解釋其中的差異 多個應用程 ...
  • 用例演示 - 創建實體 本節將演示一些示例用例並討論可選場景。 創建實體 從實體/聚合根類創建對象是實體生命周期的第一步。聚合/聚合根規則和最佳實踐部分 建議為Entity類創建一個主構造函數,以保證創建一個有效的實體。因此,無論何時我們需要創建實體的實例,我們都應該使用那個構造函數 參見下麵的問題 ...
  • 痞子衡嵌入式半月刊: 第 57 期 這裡分享嵌入式領域有用有趣的項目/工具以及一些熱點新聞,農曆年分二十四節氣,希望在每個交節之日準時發佈一期。 本期刊是開源項目(GitHub: JayHeng/pzh-mcu-bi-weekly),歡迎提交 issue,投稿或推薦你知道的嵌入式那些事兒。 上期回顧 ...
  • 2022年6月初合宙新上市了 Air32F103 系列 MCU, 分 Air32F103CBT6 和 Air32F103CCT6 兩個型號, 分別是 32K RAM + 128K FLASH 和 32K RAM + 256K FLASH, 支持的最高主頻216MHz, 可以Pin2Pin替換STM3... ...
  • ## 電腦性能設置 優化設置一:開啟卓越性能 其實,win10系統中有一個卓越性能的隱藏設置,它可以讓我們的電腦,在現有配置的情況下,發揮出最優良的性能。 1、 同時按住“win+R”打開運行視窗,輸入“powershell”並點擊“確定” 2、 打開命令提示符頁面後,輸入並執行以下字元,就會出現 ...
  • 寫在前面: 這幾天留校,在做一個電機驅動的項目,使用的是合肥傑發的平臺,車規級晶元AC7801/11系列晶元。 但在進行模擬和程式燒錄的時候遇到了各種問題,藉助這個機會,私下裡總結了常見的模擬與燒錄程式常見的幾種方式,以及相關的操作,希望對大家有幫助。 單片機ISP、IAP和ICP幾種燒錄方式的區別 ...
  • 對於 MySQL 資料庫作為各個業務系統的存儲介質,在系統中承擔著非常重要的職責,如果資料庫崩了,那麼對於讀和寫資料庫的操作都會受到影響。如果不能迅速恢復,對業務的影響是非常大的。本文我將分享MySQL 雙主 + Keepalived 的高可用落地和踩坑之路。 ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...