請進行以下操作實現復現:
表結構如下:
mysql> create table xcy_test_utf8( id int ,
-> name varchar(2),
-> addr varchar(200));
會話A:
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into xcy_test_utf8 values(1,'xcy','beijing');
ERROR 1406 (22001): Data too long for column 'name' at row 1
發現name欄位太短, 然後通知DBA或者自己進行修改。
會話B:
alter table xcy_test_utf8 modify name varchar(30);
沒有馬上執行完成, 還以為是表太大,等等。。。。。。
已經過了一會兒了, 發現還沒有執行完,是不是不對勁?通過其他會話執行show processlist ,發現了 Waiting for table metadata lock.
為什麼會有 Waiting for table metadata lock 的現象?原因很簡單, 因為有事物沒有提交。嗯,確實如此,因為剛才執行了insert語句, 因為name列定義的長度太短,然後報錯了。 目前還沒有發起回滾命令 。
但是,作為後臺的資料庫管理人員,怎麼去發現有未提交的事務?作者知道的手法如下:(如果有朋友知道更多方法,請留言)
方法1:show engine innodb status \G
沒有發現狀態為active(未提交) 的事務。
方法二:查詢information_schema.innodb_trx 表
沒有發現有事務。
兩個方式都沒有發現活躍事務,但alter 語句卻被活活堵塞了。文章快寫完了,alter語句還在執行呢。 自然也會堵塞其他對這個表進行更新操作的會話,形成一個堵塞隊列。
如果是默認的lock_wait_timeout , alter 語句將在31536000 秒後鎖超時。
所以,如果發現您的alter 語句被莫名其妙的堵塞了,通過上面的方法還沒有發現是被誰堵塞了,就別慌了,也不用懷疑您自己的技能問題。
當前處理方法:
儘快取消或者kill執行alter語句的會話,以免造成堵塞隊列,影響系統的可用性。
如果alter語句必須現在就執行,通知應用管理人員後,通過show processlist 命令,找出所有狀態為Sleep的會話,然後全部殺掉。(一個一個殺也行,直到alter語句可以執行為止,但如果手工做,速度慢.)
3. 另外,建議在執行alter語句之前,修改會話級lock_wait_timeout參數,例如設置10秒鐘,10秒鐘還沒有獲得metadata 鎖,將自動鎖超時, 以免長時間堵塞對這個表的dml操作。
4.對於研發人員,執行sql失敗時,請一定儘快執行rollback命令。
猜測bug出現的原因:
在執行SQL失敗後,沒有清理該SQL所獲取的metadata 鎖。 但如果修補的話,需要考慮mysql目前是半回滾機制,只回滾當前出錯的sql,而不是整個事務(整個事務回滾需要執行rollback命令),所以也不能在sql執行失敗時,就直接清理掉metadata 鎖,需要有個判斷機制。
因為在5.7.18上能夠100%的復現,後續將向官方提bug.