sql >> データベース >  >> RDS >> Mysql

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

    受け入れられている解決策は、残念ながら間違っています 。それはそれが言う限り正しいです、

    これは確かに(ほぼ もちろん;以下を参照)何をすべきか。しかし、それは示唆しています、

    ...そして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を使用できます 利用可能な場合(まもなく廃止されます。回避策があります PDOの場合。 または それをチェックするかもしれません エラーが発生し、発生した場合は接続を再開します(これはPythonの方法です)。つまり、少額の成功報酬で、それは実行可能です。

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

    現存する結果セットのリソースを解放するためにそれらを照会する方法がわかりません。唯一の方法は保存することです プライベートアレイ内のこれらのリソース。



    1. AnsibleGalaxyのPostgreSQLPlanet

    2. テーブル定義を表示するT-SQLクエリ?

    3. mySQLでテーブル全体を検索して文字列を探します

    4. Codeigniterを使用した複数の画像のアップロードにより、MySQLデータベースへのファイルパスが1つだけ保存されます