MySQL?中定位?DDL?被阻塞的問(wèn)題及解決方案
DDL 被阻塞了,如何找到阻塞它的 SQL?
經(jīng)常碰到開(kāi)發(fā)、測(cè)試童鞋會(huì)問(wèn),線(xiàn)下開(kāi)發(fā)、測(cè)試環(huán)境,執(zhí)行了一個(gè)DDL,發(fā)現(xiàn)很久都沒(méi)有執(zhí)行完,是不是被阻塞了?要怎么解決?
包括在群里,也經(jīng)常會(huì)碰到類(lèi)似問(wèn)題:DDL 被阻塞了,如何找到阻塞它的 SQL ?
實(shí)際上,如何解決 DDL 被阻塞的問(wèn)題,是 MySQL 中一個(gè)共性且高頻的問(wèn)題。
下面,就這個(gè)問(wèn)題,給一個(gè)清晰明了、拿來(lái)即用的解決方案:
怎么判斷一個(gè)DDL是不是被阻塞了 ?當(dāng)DDL被阻塞時(shí),怎么找出阻塞它的會(huì)話(huà) ?
怎么判斷一個(gè) DDL是不是被阻塞了?
首先,看一個(gè)簡(jiǎn)單的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)
判斷一個(gè) DDL 是不是被阻塞了,很簡(jiǎn)單,就是執(zhí)行 show processlist ,查看 DDL 操作對(duì)應(yīng)的狀態(tài)。
如果顯示的是 Waiting for table metadata lock ,則意味著這個(gè) DDL 被阻塞了。
DDL 一旦被阻塞了,后續(xù)針對(duì)該表的所有操作都會(huì)被阻塞,都會(huì)顯示 Waiting for table metadata lock 。這也是 DDL 讓人聞之色變的原因。
碰到了類(lèi)似場(chǎng)景,要么 Kill DDL 操作,要么 Kill 阻塞 DDL 的會(huì)話(huà)。
Kill DDL 操作是一個(gè)治標(biāo)不治本的方法,畢竟 DDL 操作總要執(zhí)行。
除此之外,對(duì)于 DDL 操作,需要獲取元數(shù)據(jù)庫(kù)鎖的階段有兩個(gè):DDL 開(kāi)始之初和 DDL 結(jié)束之前。如果是后者,就意味著之前的操作都要回滾,成本相對(duì)較高。
所以,碰到類(lèi)似場(chǎng)景,我們一般都會(huì) Kill 阻塞 DDL 的會(huì)話(huà)。
那么,怎么知道是哪些會(huì)話(huà)阻塞了 DDL 呢?
下面我們看看具體的定位方法。
定位方法
方法一:sys.schema_table_lock_waits
sys.schema_table_lock_waits 是MySQL 5.7引入的,用來(lái)定位 DDL 被阻塞的問(wèn)題。
針對(duì)上面這個(gè)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)只有一個(gè) alter 操作,卻產(chǎn)生了兩條記錄,而且兩條記錄的 Kill 對(duì)象還不一樣,其中一條 Kill 的對(duì)象還是 alter 操作本身。
如果對(duì)表結(jié)構(gòu)不熟悉或不仔細(xì)看記錄內(nèi)容的話(huà),難免會(huì) Kill 錯(cuò)對(duì)象。
不僅如此,在 DDL 操作被阻塞后,如果后續(xù)有 N 個(gè)查詢(xún)被 DDL 操作堵塞,還會(huì)產(chǎn)生 N*2 條記錄。
在定位問(wèn)題時(shí),這 N*2 條記錄完全是個(gè)噪音。
這個(gè)時(shí)候,就需要我們對(duì)上述記錄進(jìn)行過(guò)濾了。
過(guò)濾的關(guān)鍵是 blocking_lock_type 不等于 SHARED_UPGRADABLE。
SHARED_UPGRADABLE 是一個(gè)可升級(jí)的共享元數(shù)據(jù)鎖,加鎖期間,允許并發(fā)查詢(xún)和更新,常用在 DDL 操作的第一階段。
所以,阻塞DDL的不會(huì)是SHARED_UPGRADABLE。
故而,針對(duì)上面這個(gè) case,我們可以通過(guò)下面這個(gè)查詢(xún)來(lái)精確地定位出需要 Kill 的會(huì)話(huà)。
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';
方法二:Kill DDL 之前的會(huì)話(huà)
sys.schema_table_lock_waits 是 MySQL 5.7 才引入的。
但在實(shí)際生產(chǎn)環(huán)境,MySQL 5.6還是占有相當(dāng)多的份額。
如何解決MySQL 5.6的這個(gè)痛點(diǎn)呢 ?
細(xì)究下來(lái),導(dǎo)致 DDL 被阻塞的操作,無(wú)非兩類(lèi):
表上有慢查詢(xún)未結(jié)束。
表上有事務(wù)未提交。
其中,第一類(lèi)比較好定位,通過(guò) show processlist 就能發(fā)現(xiàn)。
而第二類(lèi)僅憑 show processlist 很難定位,因?yàn)槲刺峤皇聞?wù)的連接在 show processlist 中的狀態(tài)同空閑連接一樣,都是 Sleep 。
所以,網(wǎng)上有 Kill 空閑連接的說(shuō)法,其實(shí)也不無(wú)道理,但這樣做就太簡(jiǎn)單粗暴了,難免會(huì)誤殺。
其實(shí),既然是事務(wù),在 information_schema.innodb_trx中肯定會(huì)有記錄,如 session1 中的事務(wù),在表中的記錄如下,
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 是線(xiàn)程 id ,結(jié)合 information_schema.processlist ,可進(jìn)一步縮小范圍。
所以,我們可以通過(guò)下面這個(gè) SQL ,定位出執(zhí)行時(shí)間早于 DDL 的事務(wù)。
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;可喜的是,當(dāng)前正在執(zhí)行的查詢(xún)也會(huì)顯示在information_schema.innodb_trx中。
所以,上面這個(gè) SQL 同樣也適用于慢查詢(xún)未結(jié)束的場(chǎng)景。
MySQL 5.7中使用sys.schema_table_lock_waits的注意事項(xiàng)
sys.schema_table_lock_waits 視圖依賴(lài)了一張 MDL 相關(guān)的表-performance_schema.metadata_locks。
該表是 MySQL 5.7 引入的,會(huì)顯示 MDL 的相關(guān)信息,包括作用對(duì)象、鎖的類(lèi)型及鎖的狀態(tài)等。
但在 MySQL 5.7 中,該表默認(rèn)為空,因?yàn)榕c之相關(guān)的 instrument 默認(rèn)沒(méi)有開(kāi)啟。MySQL 8.0 才默認(rèn)開(kāi)啟。
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 ,必須首先開(kāi)啟 MDL 相關(guān)的 instrument。
開(kāi)啟方式很簡(jiǎn)單,直接修改 performance_schema.setup_instruments 表即可。
具體SQL如下。
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME = 'wait/lock/metadata/sql/mdl';
但這種方式是臨時(shí)生效,實(shí)例重啟后,又會(huì)恢復(fù)為默認(rèn)值。
建議同步修改配置文件。
[mysqld] performance-schema-instrument='wait/lock/metadata/sql/mdl=ON'
總結(jié)
1. 執(zhí)行 show processlist ,如果 DDL 的狀態(tài)是 Waiting for table metadata lock ,則意味著這個(gè) DDL 被阻塞了。
2. 定位導(dǎo)致 DDL 被阻塞的會(huì)話(huà),常用的方法有兩種:
2.1 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 相關(guān)的 instrument 默認(rèn)沒(méi)有打開(kāi)。
2.2 Kill DDL 之前的會(huì)話(huà)
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 相關(guān)的 instrument 沒(méi)有打開(kāi)或在 MySQL 5.6 中,可使用該方法。
到此這篇關(guān)于MySQL 中如何定位 DDL 被阻塞的問(wèn)題的文章就介紹到這了,更多相關(guān)MySQL定位 DDL 被阻塞內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mysql version can not be less than 4.1 出錯(cuò)解決辦法
這篇文章主要介紹了Mysql version can not be less than 4.1 解決辦法的相關(guān)資料,需要的朋友可以參考下2016-10-10
MySQL切分函數(shù)substring()的具體使用
這篇文章主要介紹了MySQL切分函數(shù)substring()的具體使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02
mysql查找刪除表中重復(fù)數(shù)據(jù)方法總結(jié)
在本篇文章中小編給大家整理了關(guān)于mysql查找刪除表中重復(fù)數(shù)據(jù)方法和相關(guān)知識(shí)點(diǎn),需要的朋友們參考下。2019-05-05
windows下修改Mysql5.7.11初始密碼的圖文教程
這篇文章主要介紹了windows下修改Mysql5.7.11初始密碼的圖文教程,本文給大家介紹的非常詳細(xì),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11
MySQL版本低了不支持兩個(gè)時(shí)間戳類(lèi)型的值解決方法
在本篇文章里小編給大家分享了關(guān)于MySQL 版本低了,不支持兩個(gè)時(shí)間戳類(lèi)型的值的相關(guān)知識(shí)點(diǎn),有興趣的朋友們可以參考下。2019-09-09
5個(gè)常用的MySQL數(shù)據(jù)庫(kù)管理工具詳細(xì)介紹
本篇文章是對(duì)5個(gè)常用的MySQL數(shù)據(jù)庫(kù)管理工具進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06

