MySQLにおけるデッドロックの調査方法

デッドロックの概要
デッドロックとは、それぞれが他の必要なロックを保持しているために、異なるトランザクションを続行できない状況です。 両方のトランザクションがリソースが使用可能になるのを待機しているため、保持しているロックは解放されません。
- 互いにロックの解放待ちを行う場合に発生する。
- デッドロックが発生するのは、ロックの待機を実施する排他ロックが1つでも含まれる場合

共有ロックと排他ロック
- 共有ロック = sロック:読み取りを行なっているため、他からの更新を制限するロック
- 排他ロック = xロック:更新中のため、他からの更新を制限するロック
共有ロックは複数のトランザクションが取得できるが、排他ロックは単一のトランザクションしか取得できない。

共有ロックと排他ロックによるデッドロック
次のようなケースで発生する。
- トランザクションAが共有ロック取得
- トランザクションBが共有ロック取得
- トランザクションAが排他ロック取得(待機)
- トランザクションBが排他ロック取得(待機)
この時点でデッドロックが発生し、トランザクションBがロールバックされる(必ずトランザクションBなのか自信がない)

排他ロック同士のデッドロック
相手がロックを持っているリソースに対してそれぞれ排他ロックを取得すると、デッドロックが発生する。

ロックの範囲
いくつかあるみたいだけど、基本的にはテーブルロックと行ロック

インテンションロック
インテントロックは、トランザクションが後でテーブルの行に必要とするロックのタイプ (共有または排他) を示すテーブルレベルのロックです。 インテントロックには、次の 2 種類があります:
intention shared lock (IS) は、トランザクションがテーブルの個々の行に shared ロックを設定することを示します。
intention exclusive lock (IX) は、トランザクションがテーブル内の個々の行に排他ロックを設定することを示します。
intent の意味の通りで、意図を示すためのロック。
SELECT ... FOR SHARE
はISロックを、SELECT ... FOR UPDATE
はIXロックを取得する。
意図的ロックでは、完全なテーブルリクエスト (LOCK TABLES ... WRITE など) 以外はブロックされません。 意図的ロックの主な目的は、誰かが行をロックしていること、またはテーブル内の行をロックしていることを示すことです。
あ〜、テーブルロックと行ロックで粒度が異なるけど、行ロックであっても「このテーブルに対してロックがかかっているらしい」という意図が伝わることで、テーブルロックが正しく動作できるようになるということか。

InnoDB におけるレコードロック
条件に合致した行に対するロックではなく、スキャンしたインデックスに対するロック。
レコードロックでは、テーブルにインデックスが定義されていなくても必ず、インデックスレコードがロックされます。 このような場合は、InnoDB によって非表示のクラスタ化されたインデックスが作成され、このインデックスを使用してレコードロックが行われます。
レポートでは次のように見えるそう。
RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t`
trx id 10078 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 8000000a; asc ;;
1: len 6; hex 00000000274f; asc 'O;;
2: len 7; hex b60000019d0110; asc ;;

これがわかりやすかった

ギャップロック
複数の範囲にかかるロック。
明示的に WHERE句で指定するのはわかりやすいが、注意したいのは存在しないレコードへの更新時、インデックスのはざますべてに対してロックがかかること。