在上一篇文章《MySQL 5.7中如何定位DDL被阻塞的問題》中,對於DDL被阻塞問題的定位,咱們主要是基於MySQL 5.7新引入的performance_schema.metadata_locks表。提出的定位方法,很有種"錦上添花"的意味,並且,也只適用於MySQL 5.7開始的版本。mysql
但在實際生產中,MySQL 5.6仍是佔毫不多數。雖然MySQL 8.0都已經GA了,但鑑於數據庫的特殊性,在對待升級的這個事情上,至關一部分人仍是秉持着一種「不主動」的態度。sql
既然MySQL 5.6用者衆多,有沒有一種方法,來解決MySQL 5.6的這個痛點呢?數據庫
仍是以前的測試Demosession
會話1開啓了事務並執行了三個操做,但未提交,此時,會話2執行了alter table操做,被阻塞。測試
session1> begin; Query OK, 0 rows affected (0.00 sec) session1> delete from slowtech.t1 where id=2; Query OK, 1 row affected (0.00 sec) session1> select * from slowtech.t1; +------+------+ | id | name | +------+------+ | 1 | a | +------+------+ row in set (0.00 sec) session1> update slowtech.t1 set name='c' where id=1; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 session2> alter table slowtech.t1 add c1 int; ##被阻塞 session3> show processlist; +----+------+-----------+------+---------+------+---------------------------------+------------------------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+------+-----------+------+---------+------+---------------------------------+------------------------------------+ | 2 | root | localhost | NULL | Sleep | 51 | | NULL | | 3 | root | localhost | NULL | Query | 0 | starting | show processlist | | 4 | root | localhost | NULL | Query | 9 | Waiting for table metadata lock | alter table slowtech.t1 add c1 int | +----+------+-----------+------+---------+------+---------------------------------+------------------------------------+ rows in set (0.00 sec)
其實,致使DDL阻塞的操做,無非兩類: spa
1. 慢查詢 線程
2. 表上有事務未提交code
其中,第一類比較好定位,經過show processlist即能發現。而第二類基本無法定位,由於未提交事務的鏈接在show processlist中的輸出同空閒鏈接同樣。orm
以下面Id爲2的鏈接,雖然Command顯示爲「Sleep」,實際上是事務未提交。blog
mysql> show processlist; +----+------+-----------+------+---------+------+---------------------------------+------------------------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+------+-----------+------+---------+------+---------------------------------+------------------------------------+ | 2 | root | localhost | NULL | Sleep | 77 | | NULL | | 3 | root | localhost | NULL | Query | 0 | starting | show processlist | | 4 | root | localhost | NULL | Query | 44 | Waiting for table metadata lock | alter table slowtech.t1 add c1 int | +----+------+-----------+------+---------+------+---------------------------------+------------------------------------+ 3 rows in set (0.00 sec)
因此,網上有kill空閒(Command爲Sleep)鏈接的說法,其實也不無道理,但這樣作就太簡單粗暴了,不免會誤殺。
其實,既然是事務,在information_schema. innodb_trx中確定會有記錄,如會話1中的事務,在表中的記錄以下,
mysql> select * from information_schema.innodb_trx\G *************************** 1. row *************************** trx_id: 1050390 trx_state: RUNNING trx_started: 2018-07-17 08:55:32 trx_requested_lock_id: NULL trx_wait_started: NULL trx_weight: 4 trx_mysql_thread_id: 2 trx_query: NULL trx_operation_state: NULL trx_tables_in_use: 0 trx_tables_locked: 1 trx_lock_structs: 2 trx_lock_memory_bytes: 1136 trx_rows_locked: 3 trx_rows_modified: 2 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 1 row in set (0.00 sec)
其中trx_mysql_thread_id是線程id,結合performance_schema.threads,能夠知道當前哪些鏈接上存在着活躍事務,這樣就進一步縮小了可被kill的線程範圍。
但從影響程度上,和kill全部Command爲Sleep的鏈接沒太大區別,畢竟,kill真正的空閒鏈接對業務的影響不大。
此時,依然能夠藉助performance_schema. events_statements_history表。
在上篇MySQL 5.7的分析中,咱們是首先知道引起阻塞的線程ID,而後利用events_statements_history表,查看該線程的相關SQL。
而在MySQL 5.6中,咱們並不知道引起阻塞的線程ID,可是,咱們能夠反其道而行之,利用窮舉法,首先統計出全部線程在當前事務執行過的全部SQL,而後再判斷這些SQL中是否包含目標表。
具體SQL以下,
SELECT processlist_id, sql_text FROM ( SELECT c.processlist_id, substring_index( sql_text, "transaction_begin;",-1 ) sql_text FROM information_schema.innodb_trx a, ( SELECT thread_id, group_concat( CASE WHEN EVENT_NAME = 'statement/sql/begin' THEN "transaction_begin" ELSE sql_text END ORDER BY event_id SEPARATOR ";" ) AS sql_text FROM performance_schema.events_statements_history GROUP BY thread_id ) b, performance_schema.threads c WHERE a.trx_mysql_thread_id = c.processlist_id AND b.thread_id = c.thread_id ) t WHERE sql_text LIKE '%t1%'; +----------------+---------------------------------------------------------------------------------------------------------+ | processlist_id | sql_text | +----------------+---------------------------------------------------------------------------------------------------------+ | 2 | delete from slowtech.t1 where id=2;select * from slowtech.t1;update slowtech.t1 set name='c' where id=1 | +----------------+---------------------------------------------------------------------------------------------------------+ 1 row in set (0.01 sec)
從輸出來看,確實也達到了預期效果。
須要注意的是,在MySQL5.6中,events_statements_history默認是沒有開啓的。
mysql> SELECT * FROM performance_schema.setup_consumers WHERE NAME LIKE '%statements%'; +--------------------------------+---------+ | NAME | ENABLED | +--------------------------------+---------+ | events_statements_current | YES | | events_statements_history | NO | | events_statements_history_long | NO | | statements_digest | YES | +--------------------------------+---------+ 4 rows in set (0.00 sec)