web-dev-qa-db-ja.com

MySQL:「テーブルメタデータのロックを待機しています」が永続的に発生する

私のMySQLデータベースは、ストレージバックエンドとして3つのwebappを提供しています。しかし、最近「テーブルメタデータロックを待機しています」というエラーが恒久的に発生します。それはほとんどいつも起こり、私はなぜか分かりません。

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」のテーブル構造を作成しようとするプログラムを再起動すると、新しく作成されたプロセスが再びmetaockで終了します。

データベースを削除することもできません。

mysql> drop database bookmaker3;

これにより、メタロックも生成されます。

どうすれば修理できますか?

14
toom

ロックで接続を強制終了します

Kill 1398

次に、自動コミットが0に設定されているかどうかを確認します。

select @@autocommit;

はいの場合、おそらくトランザクションをコミットするのを忘れていました。次に、別の接続がこのテーブルで何かを実行しようとすると、ロックが発生します。

あなたの場合:LogEntries(存在するかどうか)に対してクエリを実行し、それをコミットしなかった場合、別の接続からCREATE TABLE IF NOT EXISTSを実行しようとしました-メタデータロックが発生します。

edit私にとって、バグはアプリケーションのどこかにあります。そこを確認するか、アプリケーションでトランザクションを使用していない場合は、autocommitを1に設定します。

psもこの投稿を確認します。

19
michalczukm

受け入れられた解決策は、残念ながら wrong です。それが言う限りは正しい

ロックで接続を強制終了

これは確かに何をすべきかです。しかし、それは示唆しています、

Kill 1398

...そして1398 is notロックとの接続。それはどうでしょうか?ロック待ちの接続です。これは、ロックが /にまだないことを意味します!それを殺しても何も役に立たない(実際には another 問題の一部を解決するかもしれないが、一時的にしか解決しないかもしれない)。

本当の原因は、別のプロセスがロックを保持しているであり、さらに重要なことに、SHOW FULL PROCESSLISTはそれが何であるかを通知しません。

それは[〜#〜]する[〜#〜]プロセスが /しているかどうかを伝えます、はい。通常は機能します。ここでは、ロックを保持しているプロセスは何も実行しておらず、他のスレッドの間で何も実行していないことを隠しています。

迅速かつ汚い解決策、実際には推奨されません

「スリープ」状態の all プロセスを、同じデータベース上で、「メタデータのロック待ち」状態の最も古いスレッドであるプロセスを強制終了します。これが Arnaud Amaury が実行する内容です。

  • wFLに少なくとも1つのスレッドがある各データベース:
    • そのDB上のWFLで最も古い接続がZ秒前であることが判明
    • そのDB上でZより古いすべての「スリープ」スレッドを実行する必要があります。

より集中した修正

SHOW ENGINE INNODB STATUSを実行して、「トランザクション」セクションを確認します。あなたは、とりわけ、次のようなものを見つけるでしょう

TRANSACTION 1701, ACTIVE 58 sec;2 lock struct(s), heap size 376, 1 row lock(s), undo log entries 1
MySQL thread id 1396, OS thread handle 0x7fd06d675700, query id 1138 hostname 1.2.3.4 whatever;

次に、SHOW FULL PROCESSLISTでスレッドID 1396が何をしているのかを確認します。おそらく「スリープ」ステータスになっています。つまり、アクティブなロックが設定されたアクティブなトランザクションです。元に戻すログエントリがあるため、変更が加えられていますが、現在はアイドル状態です。 それそして、あなたが殺す必要があるスレッドは他にありません。

実際にLinuxを使用していて、root権限を持っている場合、どの process がロックを要求した接続を持っているかを確認する方法があります。これにより、そのプロセスが実際に何かを実行しているかどうかを(CPU使用率から、最悪の場合はstrace -ff -p pidから)判断して、安全に強制終了できるかどうかを判断できます。

「永続的」または「プールされた」MySQL接続を使用するwebappsでこれが発生するのを確認します。これにより、今日はほとんど時間を節約できません。webappインスタンスは終了しましたが、接続は行われませんでしたため、ロックはまだ有効です。他の全員をブロックします。

最近のMySQLを使用している場合に原因を取得するもう1つの方法は、最近ではありませんが、これは非推奨になる予定ですであるため、(情報スキーマ)

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS 
     WHERE LOCK_TRX_ID IN 
        (SELECT BLOCKING_TRX_ID FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS);

実際の解決策

問題は通常、次のアーキテクチャが原因です。

webapp (jar, php) --> container (mysqldb, php_module) --> web server --> MySQL

Webappが停止するか、webapp軽量スレッドインスタンスが停止すると、コンテナはしません。そして、接続を開いたままにするのはコンテナなので、接続は閉じません。 MySQLは操作を完了したとは考えていません。

Webアプリケーションが自動的にクリーンアップしなかった場合(トランザクションに対してROLLBACKまたはCOMMITがない、UNLOCK TABLESがないなど)、そのWebアプリケーションが開始したものは何でもは現在も存在し、であり、それでも他のすべてのユーザーをブロックしている可能性があります。

次に、2つの解決策があります。悪いのは アイドルタイムアウトを小さくする です。しかし、2つのクエリ間で待機時間が長すぎるとどうなるかを推測します(正確には、「MySQLサーバーがなくなった」。しかし that そして接続を再開します。そのため-わずかなパフォーマンス料金で実行可能です)。

より優れた、よりスマートなソリューションは、実装が簡単ではありません。すべての例外を確実にキャッチして適切に処理できるように、スクリプトを自動的にクリーンアップするか、または可能な場合は永続的な接続を完全にスキップしてください。各インスタンスに独自の接続を作成させるか、 スマートプールドライバーを使用 (PHP PDOの場合、PDO::ATTR_PERSISTENTを明示的に使用してfalseに設定) 。別の方法(PHPなど)では、トランザクションをコミットまたはロールバックし、明示的なテーブルのロック解除を発行することで、破壊ハンドラと例外ハンドラで接続を強制的にクリーンアップできます。

0
LSerni

HSプラグインがあり、すでにHSを介して評価しようとしたCREATEまたはALTERテーブルを試行する場合、同様の問題に直面し、リリースするためにこの方法でHSプラグインを再起動する必要があります。テーブルメタデータロック:

UNINSTALL PLUGIN HANDLERSOCKET;
INSTALL PLUGIN HANDLERSOCKET SONAME 'handlersocket.so';
0