ストレージエンジンは InnoDB. MySQL バージョンは 8.0.33
目次
- 前置き - ロックの粒度
- A. データベースロック
- B. テーブルロック
- C. ページロック
- D. 行ロック
- 本題 - インテンションロック
- 想定外ロックを避けるには
前置き - ロックの粒度
( この辺知ってる、という方は 本題 - インテンションロック まで進んでください )
ロックの粒度は次の4つ
- A. データベースロック
- B. テーブルロック
- C. ページロック
- D. 行ロック
図にすると次の通り。
A. データベースロック
MySQL では明示的にデータベースロックする「コマンド」や「設定値」はない。
以下 2 つでそれに近い状態が出来上がる。
疑似的なロック
FLUSH TABLES WITH READ LOCK;
この SQL は以下の特性を持つ。
- テーブル全体に向けて
- 書込を禁止する
- 読取を許可する
UNLOCK TABLES;
で解除可能。
長く続くトランザクション
意図的でない限り、 一つのトランザクションが延々と続く場合は
他トランザクションをロックする可能性を高くする。
その意味で、データベースロックと似た状況が生じる。
- 例1.) 何千万、何億とある SQL を一つのトランザクションで処理する
- 例2.) インデックス設計が不適切で、何十分経っても結果を取得できない
B. テーブルロック
テーブル全体に対するロック
例えば、テーブル内の全行を一括更新・削除する場合に発生する。
C. ページロック
ここでは省略。
「述語ロック(Predicate Lock)」がこれに対応するが、他のロックに比べれば発生頻度(利用頻度)は低いと思われるため。
補足:ページとは
データを保存・管理する基本単位のことで「コンテナ」がイメージとして近い。
以下 4 種類のデータを格納する。
- テーブルの行データ
- インデックスデータ
- トランザクション情報
- (ページそのものの)メタデータ
D. 行ロック
行に対するロック
ロックの範囲が行単位である。
本題 - インテンションロック
インテンションロックとは、
- トランザクションがテーブルに対して取得するロックのこと
- B. のテーブルロックとは別物
図解すると以下の通り
その1 - 最初にインテンションロックを確認する
- トランザクションは、テーブルに「インテンションロック」があるか確認する
その2 - テーブルにインテンションロック・行にロックを取得
確認の結果、テーブルにインテンションロックがないので
- テーブルにインテンションロックを取得
- 行にロックを取得
- トランザクションが続く限り、上 2 つのロックは継続する
その3 - 別トランザクションがインテンションロックを確認
別トランザクションが、テーブルのインテンションロックを確認する
- 行ロック可能かを テーブルのインテンションロックから確認する
- (できないので)先約のトランザクション完了待ち
- いわゆるロック状態・ロック解放待ち
できる・できないの突合はドキュメントのマトリクスを参照
その4 - トランザクション再開後、先にインテンションロックする
先約トランザクション完了 = ロック解放されたら、次のトランザクション用に
- テーブルにインテンションロックを取得
- 行にロックを取得
なぜ、インテンションロックが必要か
処理効率化のため。
例えば、テーブル T1 の行数(レコード数) が 5,000万あると仮定する。
インテンションロックがない場合、インデックスが適切でない SQL 等は
「全行の検査」が必要。逐次処理では非常に無駄なため、
インテンションロックが設けられている。
想定外ロックを避けるには
- 長いトランザクション
- インテンションロック
が組み合わさると、予期しないロックが発生する。なので、次に留意したい
- 特に理由がない限り、トランザクションは短くすること
- 取得と更新のトランザクションは分けるなど
- インデックス設計を見直して、高速化を図ること
- 性能テスト・負荷テストをして以下を確認すること
- 長いトランザクションが発生しないか
- 競合しあうトランザクションは発生しないか