我的 MySQL 資料庫為三個 Web 應用程式提供儲存後端服務。然而,我最近永久遇到了錯誤“等待表元數據鎖”。這種情況幾乎一直發生,我不明白為什麼。
mysql> show processlist -> ; +------+-----------+-----------------+------------+---------+------+---------------------------------+------------------------------------------------------------------------------------------------------+ | Id | User | Host | db | Command | Time | State | Info | +------+-----------+-----------------+------------+---------+------+---------------------------------+------------------------------------------------------------------------------------------------------+ | 36 | root | localhost:33444 | bookmaker2 | Sleep | 139 | | NULL | | 37 | root | localhost:33445 | bookmaker2 | Sleep | 139 | | NULL | | 38 | root | localhost:33446 | bookmaker2 | Sleep | 139 | | NULL | | 39 | root | localhost:33447 | bookmaker2 | Sleep | 49 | | NULL | | 40 | root | localhost:33448 | bookmaker2 | Sleep | 139 | | NULL | | 1315 | bookmaker | localhost:34869 | bookmaker | Sleep | 58 | | NULL | | 1316 | root | localhost:34874 | bookmaker3 | Sleep | 56 | | NULL | | 1395 | bookmaker | localhost:34953 | bookmaker | Sleep | 58 | | NULL | | 1396 | root | localhost:34954 | bookmaker3 | Sleep | 46 | | NULL | | 1398 | root | localhost:34956 | bookmaker3 | Query | 28 | Waiting for table metadata lock | CREATE TABLE IF NOT EXISTS LogEntries ( lid INT NOT NULL AUTO_INCREMEN | | 1399 | root | localhost | NULL | Query | 0 | NULL | show processlist | +------+-----------+-----------------+------------+---------+------+---------------------------------+------------------------------------------------------------------------------------------------------+
當然可以殺死相應的進程。但是,如果我重新啟動嘗試建立資料庫「bookmaker3」的表結構的程序,新建立的進程將再次處於 Metallock 中。
我甚至無法刪除資料庫:
mysql> drop database bookmaker3;
這也會產生金屬鎖。
如何修復這個問題?
不幸的是,所接受的解決方案是錯誤的。說的一點都沒錯
這確實是(幾乎肯定;見下文)要做的事情。但隨後它表明,
...而1398不是與鎖的連接。怎麼會這樣? 1398 是正在等待鎖定的連線。這意味著它還沒有獲得鎖,因此殺死它沒有任何作用。持有鎖的程序仍將持有鎖,並且下一個執行緒嘗試執行某些操作將因此也停止並以適當的順序進入「等待元資料鎖」。
您無法保證「等待元資料鎖定」(WFML) 的進程也不會阻塞,但您可以確定僅終止 WFML 進程將完全無濟於事。
真正的原因是另一個進程正在持有鎖定,更重要的是,
SHOW FULL PROCESSLIST
不會直接告訴你是哪個進程.您可以確定的一件事是,沒有標記為「等待元資料鎖定」的進程。可以說,這些人是受害者。
SHOW FULL PROCESSLIST
WILL 告訴您進程是否正在做某事,是的。通常它會起作用。在這裡,持有鎖的進程什麼都不做,並且隱藏在其他也不做任何事情的執行緒中並報告為「睡眠」。如果
SHOW FULL PROCESSLIST
向您顯示一個正在執行DML的進程,或是處於「傳送資料」狀態,那麼,那幾乎肯定是罪魁禍首。其他進程正在等待它釋放鎖(它們可以是隱式鎖;進程根本不需要發出 LOCK TABLE,這實際上會以不同的方式鎖定)。但是一個進程可以在不執行任何操作時持有鎖,並被適當地標記為「睡眠」。在OP的情況下,罪魁禍首幾乎肯定是進程1396,它在進程1398之前啟動,現在處於
睡眠
狀態,並且已經已經持續了46 秒。由於1396 顯然已經完成了它需要做的所有事情(事實證明它現在正在休眠,並且已經這樣做了46 秒,就MySQL 而言),沒有線程進入在它可以持有鎖並仍然持有它之前睡眠(否則1396 也會停止)。由於MySQL的「無死鎖」鎖定策略,任何進程都不能持有鎖、釋放鎖並再次恢復鎖定;因此,鎖定等待總是由仍然持有鎖且之前從未持有過該鎖的進程引起。這很有用(我們將在下面利用這個事實),因為它可以保證鎖定「隊列」是順序的。
重要:如果您以受限使用者身分連接到 MySQL,
SHOW FULL PROCESSLIST
將不會顯示所有進程。因此鎖可能由您看不到的進程持有。所以:如果
SHOW FULL PROCESSLIST
向您顯示所有內容並顯示一個正在運行進程,那麼該進程可能是負責的,您需要等待它完成它正在做的任何事(或你可以殺死它- 後果自負)。這個答案的其餘部分涉及一種令人困惑的情況,即進程正在等待沒有明顯的原因並且似乎沒有人在做任何事情。
更好的
#顯示進程清單
上面可以調整為僅顯示處於SLEEP 狀態的進程,並且無論如何它都會按時間降序對它們進行排序,因此更容易找到掛起的進程(由於順序,通常是
在「等待元數據鎖定」之前立即睡眠一個;並且它總是比任何等待的睡眠時間都多的睡眠之一。
重要的事情
保留任何「等待元資料鎖定」進程單獨。
快速而骯髒的解決方案,不真正推薦,但很快
殺死同一資料庫上所有處於「睡眠」狀態的進程,這些進程比最舊的執行緒處於「等待元資料鎖定」狀態。這就是Arnaud Amaury 會這麼做:
KILL
後,重新評估情況並相應地重新啟動進程。正在等待的進程現在可能正在運行,或者它們可能已經短暫運行並現在正在休眠。 他們現在甚至可能持有新的元資料鎖定。一百次中有九十九次,要殺死的線程是處於睡眠狀態且比等待元數據鎖定的較舊線程更老的線程中最年輕的線程: