【MySQL經典案例分析】 Waiting for table metadata lock

本文由雲+社區發表

1、 問題是這樣來的

​ 2018年某個週末,接到連續數據庫的告警,告警信息以下:python

img

2、 苦逼的探索過程

一、整體的思路

看到too many connection的報錯信息,基本上能夠把問題定位在:mysql

(1)機器負載飆升,致使SQL執行效率降低,致使鏈接推積sql

(2)業務訪問量突增(或者有SQL注入現象),致使鏈接數打滿數據庫

(3)出現「死鎖」或者鎖競爭嚴重,致使大量SQL堆積服務器

二、排查過程

(1)機器的各項性能指標都顯示正常, 沒有出現高負載現象,暫時先排除了這種緣由session

(2)查看監控信息,發如今鏈接數打滿的時間點前並無訪問量突增的趨勢,同時經過檢查告警信息並無發現有注入工單併發

img

(3)最後上到服務器上查看下SQL的執行狀況性能

①查看show full processlist;spa

img

​ 大量的請求都是在「Waiting for table metadata lock」,能夠分紅三類請求:code

  • Select請求
  • Rename請求
  • Sleep請求

    ②分析Waiting for table metadata lock

​ 通常來講常見的「Waiting for table metadata lock」會出如今DDL操做或者是有未提交的事務上,從information_schema.processlist表中,沒有發現有DDL操做,而可以產生MDL鎖的操做也只剩下rename,可是根據SQL執行的狀態,rename操做也是在等待MDL鎖,因此rename操做應該是被阻塞的操做,而不是產生MDL鎖的操做。

​ 接着咱們來查看下死鎖和事務的相關指標:

  • show engine innodb status;中沒有任何死鎖的信息
  • information_schema.innodb_trx 、information_schema.innodb_locks 、 information_schema.innodb_lock_waits 的也沒有任何形式的鎖信息。

​ 如今基本又排除了顯示的死鎖問題,那是從show full processlist中也抓不出任何請求,這裏就比較疑惑了,當看了下表的結構式,發現這個表是myisam引擎的,因此上面的兩種統計信息裏面沒有任何值就能夠解釋了。

img

​ 那麼其實問題就集中在有未結束的事務上了,這裏其實有一個誤區,當時跟開發溝通存在未關閉的事務時,開發一直認爲不可能,由於myisam表是不支持事務的,只有innodb支持事務。可是對於MDL鎖來講,5.5以後引入MDL事務級別的鎖不論對myisam仍是innodb都是生效的。

③查看未提交的事務

​ 以後查看了下系統的事務自動提交的變量,autocommit的值是ON,那說明若是是事務未提交的話只多是業務主動的開啓一個事務,而沒有commit。

img

​ 爲了驗證這個猜測,打開了general log,在log中果真發現,業務在開啓事務後,把autocommit的值設爲0了,致使必需要顯示的commit才能提交事務。

img

img

​ 這時候咱們反過頭來看一下host爲10.49.84.70的鏈接請求,因爲select的執行速度很快,並且訪問並不頻繁,因此在抽樣的show processlist中,狀態值大部分時間是「Sleep」,給問題的定位帶來了一些迷惑性的干擾。接着咱們kill掉了這個進程,果真推積的請求瞬間就執行完成了,也之間印證了剛剛上述推論。

二、問題解決

​ 在與開發同窗溝經過程中,開發同窗說庫中是myisam表因此不會主動開啓事務,在代碼裏也沒有設置autocommit=0的代碼,那麼根本緣由在哪?

​ 當咱們定位到這臺服務器上的請求都是來自python的定時腳本,使用python 操做mysql的時候,使用了其pymysql模塊,可是在進行插入操做的時候,必須使用受到提交事務。Python的pymysql模塊默認是會設置autocommit=0的。

img

​ 讓咱們來對比一下其餘一樣使用python訪問的正常鏈接請求,再斷開前都會手動的commit。

img

​ 找到緣由後有思考了下,是否是能夠在建連後就設置autocommit=1呢?這樣對於以後新變動的SQL就不要再考慮到手動commit的事情了,能夠經過在初始化鏈接池的時候,對每個鏈接進行設置,即

img

3、 延伸的一些思考

一、metadata lock

(1)MDL簡述

​ 爲了在併發環境下維護表元數據的數據一致性,在表上有活動事務(顯式或隱式)的時候,不能夠對元數據進行寫入操做。所以從MySQL5.5版本開始引入了MDL鎖(metadata lock),來保護表的元數據信息,用於解決或者保證DDL操做與DML操做之間的一致性。

​ 對於引入MDL,其主要解決了2個問題,一個是事務隔離問題,好比在可重複隔離級別下,會話A在2次查詢期間,會話B對錶結構作了修改,兩次查詢結果就會不一致,沒法知足可重複讀的要求;另一個是數據複製的問題,好比會話A執行了多條更新語句期間,另一個會話B作了表結構變動而且先提交,就會致使slave在重作時,先重作alter,再重作update時就會出現複製錯誤的現象。因此在對錶進行上述操做時,若是表上有活動事務(未提交或回滾),請求寫入的會話會等待在Metadata lock wait 。

​ 支持事務的InnoDB引擎表和不支持事務的MyISAM引擎表,都會出現Metadata Lock Wait等待現象。一旦出現Metadata Lock Wait等待現象,後續全部對該表的訪問都會阻塞在該等待上,致使鏈接堆積,業務受影響。

(2)常見MDL鎖場景

①當前有執行DML操做時執行DDL操做

② 當前有對錶的長時間查詢或使用mysqldump/mysqlpump時,使用alter會被堵住

③ 顯示或者隱式開啓事務後未提交或回滾,好比查詢完成後未提交或者回滾,DDL會被堵住

④ 表上有失敗的查詢事務,好比查詢不存在的列,語句失敗返回,可是事務沒有提交,此時DDL仍然會被堵住

二、myisam、innodb對事務的支持

​ Myisam是不支持事務的,innodb是支持事務的,這個概念其實沒有任何問題,可是這裏只的都是對於數據的事務性操做的支持,經過以下簡單的實驗能夠很清楚的理解(關於事務的相關概念和解釋就再也不贅述了,只是想區別一下mysiam不支持事務,可是主動開始事務中對Myisam的操做仍然會產生MDL鎖):

​ 在隔離級別爲RC的狀況下:

(1)myisam表

① CREATE TABLE tb2 (a int(11) DEFAULT NULL ) ENGINE=MyISAM;

② Session 1:

​ mysql> begin ;

​ mysql> insert into tb2(a) value(1);

​ (在session2的update以後)

​ mysql> select * from tb2;

​ +--------+

​ | a |

​ +--------+

​ | 3 |

​ +--------+

Session 2:

​ mysql> select * from tb2;

​ +---------+

​ | a |

​ +---------+

​ | 1 |

​ +---------+

​ mysql> update tb2 set a=3 where a=1;

​ mysql> select * from tb2;

​ +--------+

​ | a |

​ +--------+

​ | 3 |

​ +--------+

​ mysql> alter table tb2 add b int(11);

... hangs ...

(2)innodb表

①CREATE TABLE tb3 (a int(11) DEFAULT NULL ) ENGINE=INNODB;

② Session 1:

​ mysql> begin ;

​ mysql> insert into tb3(a) value(1);

​ Session 2:

​ mysql> select * from tb3;

​ Empty set (0.00 sec)

三、myisam表的另外一個BUG

(1)場景

① CREATE TABLE tb2 (a int(11) DEFAULT NULL ) ENGINE=MyISAM;

② Session 1:

​ mysql> begin ;

​ mysql> select * from tb2;

​ Session 2:

​ mysql> create table if not exists tb2(a int);

​ ... hangs ...

③查看show processlist

​ Session 1:Sleep

​ Session 2:Waiting for table metadata lock

(2)解決方式

①session 1上commit或者rollback

②另外再開一個session3 ,kill掉可疑鏈接

此文已由做者受權騰訊雲+社區發佈

搜索關注公衆號「雲加社區」,第一時間獲取技術乾貨,關注後回覆1024 送你一份技術課程大禮包!

相關文章
相關標籤/搜索