MySQL: MyISAMのテーブルに対してselectクエリを実行中に別のselectクエリがテーブルロック待ちになる

環境

症状

通常、selectクエリの実行中でも同じテーブルに対して別のselectクエリを実行することができる。ところが、selectクエリの実行中に同じテーブルに対してupdateまたはinsertクエリを実行しようとすると、それ以降そのテーブルに対してはselectクエリも実行することができなくなる。

以下の手順で再現できる。

  1. MyISAMのテーブルに対して時間のかかるselectクエリを実行する。
  2. 1のクエリが完了する前に、同じテーブルに対してupdateまたはinsertクエリを実行する。このクエリはテーブルロック待ちになり、1のクエリが完了するまで実行されない。
  3. 1のクエリが完了する前に、さらに同じテーブルに対してselectクエリを実行する。このクエリもテーブルロック待ちになり、2のクエリが完了するまで実行されない。

以降、このテーブルに対する全てのクエリがテーブルロック待ちになる。1のクエリが長時間終わらない場合、場合によってはDBの接続数が急増し、アプリがDBに接続できなくなる状態に陥る。

この挙動は以下に記載がある。

WRITE ロックは通常、更新ができるだけ早く処理されるように、READ ロックより高い優先度を持っています。 つまり、あるセッションが READ ロックを取得したあと、別のセッションが WRITE ロックを要求した場合は、WRITE ロックを要求したセッションがロックを取得して解放するまで、以降の READ ロック要求が待たされます。

dev.mysql.com

解決方法

  • MyISAMをやめてInnoDBに変換する。InnoDBでは行ロックになるため、このような問題は発生しなくなる。
  • low_priority_updates 変数に1をセットする。writeロックよりもreadロックが優先されるようになる。
  • max_write_lock_count 変数に低い値を設定する。待たされている insert, update クエリがここで指定した数を超えた場合、selectが実行されるようになる。

その他の解決方法については以下に記載がある。どうしてもMyISAMを使用する場合は更新のないテーブルに対してのみ使用すること。

dev.mysql.com