メインコンテンツへスキップ

VillageSQL is a drop-in replacement for MySQL with extensions.

All examples on this page work on VillageSQL. Install Now →
デッドロックは、2 つのトランザクションがそれぞれ相手の必要とするロックを保持し、どちらも先に進めないときに起こります。InnoDB はこれを自動的に検出し、一方のトランザクション(「犠牲者」)を ERROR 1213: Deadlock found when trying to get lock; try restarting transaction で強制終了します。

デッドロックが起こる仕組み

典型的なパターン: 2 つのトランザクションが逆の順序で行をロックします。
InnoDB のデッドロック検出器がこの循環を見つけ、ロックしているリソースが少ないほう(または行った作業が少ないほう)のトランザクションをロールバックします。

デッドロックログの読み方

最新のデッドロックは、常に InnoDB のステータス出力で確認できます。
LATEST DETECTED DEADLOCK セクションを探してください。両方のトランザクション、それぞれが保持していたロック、待っていたロック、およびどちらのトランザクションがロールバックされたかが示されます。
読み取るべき重要な点:
  • どの 2 つのクエリが競合しているか
  • どのテーブルとインデックスが関係しているか
  • どちらのトランザクションが犠牲者(ロールバックされた側)か

デッドロック防止のパターン

一貫した順序で行をロックする

上のデッドロックは、A が行 1 の次に行 2 をロックし、B が行 2 の次に行 1 をロックするために起こります。両方のトランザクションが常に ID の昇順でロックすれば、循環は形成されません。
アプリケーションコードを制御できる場合は、複数の行に触れる操作に対して一貫したロック順序を強制してください。

トランザクションを短く保つ

長いトランザクションはロックを長く保持するため、競合が発生する時間が増えます。トランザクションの中では最小限の作業だけを行ってください。

SELECT … FOR UPDATE で事前にロックする

トランザクションが行を更新する予定なら、UPDATE でロックの衝突に気づくのではなく、SELECT の時点でロックします。
これでデッドロックがなくなるわけではありませんが、ロックの順序が明示的で予測可能になります。

デッドロック時に再試行する

デッドロックは並行システムでは想定されるものです。アプリケーションの正しい対応は、ERROR 1213 を捕捉してトランザクション全体を再試行することです。
個々のステートメントを再試行しないでください。InnoDB はすでにトランザクションをロールバックしているため、トランザクション全体を最初から再試行します。

ギャップロックとネクストキーロック

InnoDB は、デフォルトの REPEATABLE READ 分離レベルでファントムリードを防ぐために、ギャップロックとネクストキーロックを使います。これらの広いロックは、デッドロックの可能性を高めます。
READ COMMITTED は行ロックのみを使い(ギャップロックなし)、デッドロックの頻度を減らします。トレードオフは、ファントムリードを許すことです。ほとんどの OLTP ワークロードでは、デッドロックの影響を受けやすい操作に対して READ COMMITTED は妥当な選択です。

ロック待ちのタイムアウト

すべてのロック競合がデッドロックというわけではありません。トランザクション B が単に A のロックを待っていて A が遅い場合、B は innodb_lock_wait_timeout 秒(デフォルト: 50)後にタイムアウトします。エラーは ERROR 1205: Lock wait timeout exceeded です。
タイムアウトはデッドロックではありません。InnoDB はブロックしている側のトランザクションをロールバックせず、待っている側だけをロールバックしました。

よくある質問

現在のすべてのロックと、誰が待っているかを見るにはどうすればよいですか?

data_locks は、現在保持または要求されているすべてのロックを示します。data_lock_waits は、どのスレッドがどのスレッドをブロックしているかを示します。

InnoDB は常にデッドロックを即座に検出しますか?

InnoDB のデッドロック検出器は、ロックの競合が発生したときに同期的に実行されます。検出はほぼ瞬時です。innodb_deadlock_detect = OFF で無効にして innodb_lock_wait_timeout だけに頼ることもでき、一部の高並行ワークロードはパフォーマンスのためにそうしますが、一般的ではありません。

すべてのデッドロックを防げますか?

いいえ。デッドロックは、ロックを行う並行トランザクションの根本的な性質です。一貫したロック順序と短いトランザクションで頻度を減らすことはでき、デッドロックに対する再試行ロジックは常に書くべきです。

トラブルシューティング

関連項目