受け入れられている解決策は、残念ながら間違っています 。それはそれが言う限り正しいです、
これは確かに(ほぼ もちろん;以下を参照)何をすべきか。しかし、それは示唆しています、
...そして1398はそうではありません ロックとの接続。どうして? 1398は待機中の接続です ロックのために。これは、まだ持っていないことを意味します ロック、したがって、それを殺しても何の役にも立ちません。ロックを保持しているプロセスは引き続きロックを保持し、次へ したがって、何かをしようとしているスレッドはまた ストールして、「メタデータロックを待機しています」と順番に入力します。
「メタデータロックを待機している」(WFML)プロセスもブロックされないという保証はありませんが、WFMLプロセスのみを強制終了しても正確に何も達成されないことは確かです。 。
本当の原因は、別のプロセスがロックを保持していることです。 、そしてさらに重要なことに、SHOW FULL PROCESSLIST
それがどれであるかを直接教えてくれません 。
なります プロセスが実行中かどうかを教えてください 何か、はい。通常は動作します。ここでは、ロックを保持しているプロセスは何もしていません 、他のスレッドの中でも非表示になり、何もしません。
この場合、犯人はほぼ確実に プロセス1396 、プロセス1398の前に開始され、現在はSleep
にあります 状態、および46秒間されています。 1396は、必要なすべてのことを明確に実行して以来(MySQLに関する限り、現在スリープ中であり、46秒間実行していることからもわかるように)、 )、それ以前にスリープ状態になったスレッドは、ロックを保持できなかった可能性があります(または、1396もストールしていました)。
重要 :制限付きユーザーとしてMySQLに接続している場合は、SHOW FULL PROCESSLIST
しません すべてのプロセスを表示します。そのため、表示されていないプロセスによってロックが保持されている可能性があります。
より優れたSHOW PROCESSLIST
SELECT ID, TIME, USER, HOST, DB, COMMAND, STATE, INFO
FROM INFORMATION_SCHEMA.PROCESSLIST WHERE DB IS NOT NULL
AND (`INFO` NOT LIKE '%INFORMATION_SCHEMA%' OR INFO IS NULL)
ORDER BY `DB`, `TIME` DESC
上記は、SLEEP状態のプロセスのみを表示するように調整できます。いずれにせよ、時間の降順でプロセスをソートするため、ハングしているプロセスを簡単に見つけることができます(通常はSleep
です)。 「メタデータロックを待機している」ものの直前にあるものを作成します。
重要なこと
「メタデータロックの待機」プロセスはそのままにします 。
迅速で汚い解決策、実際には推奨されませんが、迅速です
すべてを殺す 同じデータベース上で、最も古いよりも古い「スリープ」状態のプロセス 「メタデータロックを待機中」状態のスレッド。これは、 ArnaudAmaury です。 やっただろう:
- WaitingForMetadataLockに少なくとも1つのスレッドがあるデータベースごとに:
- そのDB上のWFMLで最も古い接続は、Z秒前であることが判明しました
- そのDB上のZより古いすべての「スリープ」スレッドを実行する必要があります。万が一に備えて、最も新鮮なものから始めてください。
- そのDBに古い接続とスリープしていない接続が1つ存在する場合は、それがロックを保持している接続である可能性がありますが、しかしそれは何かを行っています 。もちろん殺してもかまいませんが、特にUPDATE / INSERT / DELETEの場合は、自分の責任で殺してください。
100回のうち99回、殺されるスレッドは最年少 古いスリープ状態の人の中で メタデータロックを待っている古いものより:
TIME STATUS
319 Sleep
205 Sleep
19 Sleep <--- one of these two "19"
19 Sleep <--- and probably this one(*)
15 Waiting for metadata lock <--- oldest WFML
15 Waiting for metadata lock
14 Waiting for metadata lock
(*)TIMEの順序は実際にはミリ秒であるため、表示されないだけだと言われました。したがって、両方のプロセスの時間値は19ですが、最も低いプロセスは若いはずです。
より焦点を絞った修正
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
で確認します スレッドID1396はその#1701トランザクションで何をしていますか。 「スリープ」状態になっている可能性があります。つまり、アクティブなロックを持つアクティブなトランザクション(#1701)は、元に戻るログエントリがあるため、いくつかの変更を加えていますが、現在はアイドル状態です。 それ そして、あなたが殺す必要のあるスレッドは他にありません。それらの変更を失う。
MySQLで何もしないということは、一般的に何もしないという意味ではないことを忘れないでください。 MySQLからいくつかのレコードを取得し、FTPアップロード用のCSVを作成する場合、FTPアップロード中はMySQL接続がアイドル状態になります。
実際、MySQLとMySQLサーバーを使用するプロセスが同じマシン上にあり、そのマシンがLinuxを実行していて、root権限を持っている場合、どのプロセスを見つける方法があります。 ロックを要求した接続があります。これにより、(CPU使用率、または最悪の場合、strace -ff -p pid
から決定できます。 )そのプロセスが本当に 何かをするかどうか、殺しても安全かどうかを判断するのに役立ちます。
なぜこれが起こるのですか?
これは、「永続的」または「プールされた」MySQL接続を使用するWebアプリで発生します。これにより、現在は通常、ほとんど時間が節約されません。Webアプリケーションインスタンスは終了しましたが、接続は終了しませんでした 、そのため、そのロックはまだ有効です...そして他のすべての人をブロックしています。
もう1つの興味深い方法 私が見つけたのは、上記の仮説で、いくつかの行を返すクエリを実行し、そしてそれらのいくつかだけを取得するということです。 。クエリが「自動クリーン」に設定されていない場合(ただし、基盤となるDBAはそれを行います)、接続を開いたままにし、テーブルの完全なロックが通過するのを防ぎます。これは、行を選択してエラーが発生した(存在しない)かどうか(存在する必要がある)を確認することで行が存在するかどうかを確認するコードの一部で発生しましたが、実際には行を取得せずに 。
DBに質問する
最近のMySQLを使用している場合に、原因を特定する別の方法ただし、最近ではない これは廃止される予定です 、is(情報スキーマに対する特権が再度必要です)
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS
WHERE LOCK_TRX_ID IN
(SELECT BLOCKING_TRX_ID FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS);
実際の解決策、時間と労力が必要
この問題は通常、このアーキテクチャが原因で発生します:
webappが停止した場合、またはwebapp軽量スレッドインスタンスが停止した場合、コンテナ/接続プールが停止しない可能性があります 。そしてそれはコンテナです これは接続を開いたままにするので、明らかに接続は閉じません。かなり予想通り、MySQLは操作が完了したとは見なしません 。
Webアプリがそれ自体の後でクリーンアップされなかった場合(ROLLBACK
なし またはCOMMIT
トランザクションの場合、UNLOCK TABLES
はありません 、など)、そのWebアプリが実行を開始したものはすべてまだ存在します 、そしてまだ他のすべての人をブロックしている可能性があります。
その場合、2つの解決策があります。さらに悪いのは、アイドルタイムアウトを下げる
ことです。 。しかし、2つのクエリの間で長く待つとどうなるかを推測してください(正確には「MySQLサーバーがなくなった」)。その後、mysql_ping
を使用できます 利用可能な場合(まもなく廃止されます。
より優れた、よりスマートなソリューションは、実装が簡単ではありません。スクリプト自体をクリーンにし、すべての行を取得するか、すべてのクエリリソースを解放し、すべての例外をキャッチして適切に処理するようにします。または、可能であれば、永続的な接続を完全にスキップします 。各インスタンスに独自の接続を作成させるか、スマートを使用しますプールドライバー
(PHP PDOでは、PDO::ATTR_PERSISTENT
を使用します 明示的にfalse
に設定 )。または(PHPなど)、トランザクションをコミットまたはロールバックし、明示的なテーブルロック解除を発行することで、破棄ハンドラーと例外ハンドラーに接続を強制的にクリーンアップさせることができます。
現存する結果セットのリソースを解放するためにそれらを照会する方法がわかりません。唯一の方法は保存することです プライベートアレイ内のこれらのリソース。