症状

MySQLで Lock wait timeout exceeded; try restarting transaction エラーが発生し、クエリが実行できない。

結論:まずこれを確認

  1. SHOW ENGINE INNODB STATUS\G でロック待ちのトランザクションを確認
  2. SHOW PROCESSLIST で長時間実行中のクエリを特定
  3. 原因のトランザクションを KILL するか、完了を待つ

トラブルシューティングフロー

    flowchart TD
    A[Lock wait timeout発生] --> B{SHOW PROCESSLIST確認}
    B --> C{長時間クエリあり?}
    C -->|Yes| D[該当クエリを特定]
    C -->|No| E[INNODB STATUSを確認]
    D --> F{KILLして良い?}
    F -->|Yes| G[KILL プロセスID]
    F -->|No| H[完了を待つ]
    E --> I{ロック情報あり?}
    I -->|Yes| J[ロック元を特定]
    I -->|No| K[アプリケーション側を確認]
    J --> F
    G --> L[再実行して確認]
    H --> L
    K --> M[コネクション管理を確認]
  

よくある原因

  • 長時間トランザクションが未コミット - BEGIN後にCOMMIT/ROLLBACKされていない
  • 大量データの更新処理 - UPDATE/DELETEが多くの行をロック中
  • デッドロックの発生 - 複数トランザクションが互いにロック待ち
  • innodb_lock_wait_timeout が短い - デフォルト50秒では足りない場合がある
  • 外部キー制約によるロック伝播 - 親テーブルへのロックが子テーブルに影響
  • インデックス未使用のUPDATE/DELETE - テーブルスキャンで広範囲をロック
  • アプリケーションのコネクションリーク - トランザクションが放置されている

確認手順

ステップ1: 現在のプロセスを確認する

    mysql -u root -p -e "SHOW FULL PROCESSLIST\G"
  

🔍 チェックポイント: Time が大きく、StateLockedWaiting for table metadata lock のプロセスがないか確認

ステップ2: InnoDBのロック状態を確認する

    mysql -u root -p -e "SHOW ENGINE INNODB STATUS\G" | grep -A 50 "TRANSACTIONS"
  

🔍 チェックポイント: LOCK WAIT の記載があるトランザクションを探す。waiting for this lock to be granted の記述に注目

ステップ3: ロック待ちの詳細を確認する(MySQL 5.7以降)

    mysql -u root -p -e "
SELECT 
  r.trx_id AS waiting_trx_id,
  r.trx_mysql_thread_id AS waiting_thread,
  r.trx_query AS waiting_query,
  b.trx_id AS blocking_trx_id,
  b.trx_mysql_thread_id AS blocking_thread,
  b.trx_query AS blocking_query
FROM information_schema.innodb_lock_waits w
JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id\G"
  

🔍 チェックポイント: blocking_thread がロック元のプロセスID

ステップ4: MySQL 8.0以降の場合

    mysql -u root -p -e "
SELECT * FROM performance_schema.data_lock_waits\G"
  
    mysql -u root -p -e "
SELECT * FROM performance_schema.data_locks\G"
  

🔍 チェックポイント: LOCK_MODELOCK_TYPE でロックの種類を確認

ステップ5: 問題のプロセスを終了する

    mysql -u root -p -e "KILL プロセスID;"
  

🔍 チェックポイント: KILL後に SHOW PROCESSLIST で該当プロセスが消えていることを確認

ステップ6: タイムアウト値を確認・変更する

    mysql -u root -p -e "SHOW VARIABLES LIKE 'innodb_lock_wait_timeout';"
  

一時的に変更する場合:

    mysql -u root -p -e "SET GLOBAL innodb_lock_wait_timeout = 120;"
  

🔍 チェックポイント: 変更後、新しい接続から値が反映されているか確認

NG行動

  • むやみにプロセスをKILLする - 重要なバッチ処理を中断する可能性がある
  • innodb_lock_wait_timeout を極端に長くする - 問題を先送りにするだけで根本解決にならない
  • MySQLサーバーを再起動する - 全トランザクションが強制終了しデータ不整合の可能性
  • LOCK TABLESを追加で実行する - 状況を悪化させる可能性が高い
  • 原因を特定せずにリトライ処理を追加する - 同じ問題が繰り返し発生する

よくある質問(FAQ)

Q1: KILLしても大丈夫?データは壊れない?

A: InnoDBはトランザクションをロールバックするため、データの整合性は保たれる。ただし、処理中だった操作は最初からやり直しになる。

Q2: デッドロックとロックタイムアウトの違いは?

A: デッドロックは相互にロック待ちの状態で、MySQLが自動検出して一方をロールバックする。ロックタイムアウトは一定時間待っても解放されない場合に発生するエラー。

Q3: アプリケーション側で対策できることは?

A: トランザクションを短く保つ、同じ順序でテーブルにアクセスする、適切なインデックスを使用する、コネクションプーリングの設定を見直す。

関連するエラー・症状

準備中

解決しない場合