SlideShare a Scribd company logo
1 of 29
Download to read offline
外部キー制約に伴う
ロックの小話
2015/2/13 「外部キー Night」
@ichirin2501(いちりんちゃん)
1
はじめに
2
検証環境
・MySQL
5.5.28
・ストレージエンジン
InnoDB
・トランザクション分離レベル
REPEATABLE-READ, READ-COMMITTED
!
時間の都合上インデックスの話は割愛
スライドの内容
ロックおさらい

・共有と排他ロックについて

・共有と排他ロックの順序によるデッドロック例
外部キー制約に伴うロックの挙動について

・基本的なロックのかかり方

・シャドーロックの紹介(注意)

・シャドーロックによる外部キー制約時の影響
3
ロックおさらい(簡易)
• 共有ロック(LOCK_S)

共有ロック同士は互いにブロックしない

例:SELECT LOCK IN SHARE MODE
• 排他ロック(LOCK_X)

何も受け付けないぞ、排他

例:INSERT(成功), UPDATE, DELETE,

 SELECT FOR UPDATE
X S
X Conflict Conflict
S Conflict Compatible
4
大きく分けてロックは2種類
5
> BEGIN;
> SELECT * FROM player

 WHERE id = 100

 LOCK IN SHARE MODE;
> BEGIN;
トランザクションA トランザクションB
共有と排他順によるデッドロック例
6
> BEGIN;
> SELECT * FROM player

 WHERE id = 100

 LOCK IN SHARE MODE;
> BEGIN;
> SELECT * FROM player
WHERE id = 100 FOR UPDATE;
Player.id(100)に

共有ロックが取られてるため、

待たされる
トランザクションA トランザクションB
共有と排他順によるデッドロック例
待たされる
7
> BEGIN;
> SELECT * FROM player

 WHERE id = 100

 LOCK IN SHARE MODE;
> BEGIN;
> SELECT * FROM player
WHERE id = 100 FOR UPDATE;
> SELECT * FROM player
WHERE id = 100 FOR UPDATE;
DeadLock !!! Player.id(100)に

共有ロックが取られてるため、

待たされる排他->共有ロックなら
デッドロックにならない
トランザクションA トランザクションB
共有と排他順によるデッドロック例
待たされる
外部キー制約によるロック
(基本編)
8
簡単に検証
1. 外部キー制約の共有ロックを確認
2. 共有->排他順によるデッドロック例

(外部キー制約ver)
これだけは押さえておく
・INSERT時に外部キー制約される側(親)に

 共有ロックがかかる
テーブル定義の例
key Extra
id PRIMARY AUTO-INCR
player KEY-INDEX
item KEY-INDEX
key Extra
id PRIMARY AUTO-INCR
key Extra
id PRIMARY
Player
Item
PlayerItem
外部キー制約する側(子)
9
外部キー制約される側(親)
> BEGIN;
> INSERT INTO player_item

(player,item) VALUES(100, 2000);
> BEGIN;
トランザクションA トランザクションB
10
外部キー制約により、

Player.id(100), Item.id(2000)

に対して共有ロックを獲得
外部キーで共有ロックがかかるのを確認
>
> BEGIN;
> INSERT INTO player_item

(player,item) VALUES(100, 2000);
> BEGIN;
> SELECT * FROM player
WHERE id = 100 FOR UPDATE;
トランザクションA トランザクションB
11
外部キー制約により、

Player.id(100), Item.id(2000)

に対して共有ロックを獲得
Player.id(100)に

共有ロックが取られてるため、

待たされる
外部キーで共有ロックがかかるのを確認
>
待たされる
12
> BEGIN; > BEGIN;
> UPDATE player SET XXX = YYY
WHERE id = 100;
DeadLock !!!
共有->排他順によるデッドロック例(外部キーver)
トランザクションA トランザクションB
> INSERT INTO player_item

(player,item) VALUES(100, 2000);
> SELECT * FROM player
WHERE id = 100 FOR UPDATE;
Player.id(100)に

共有ロックが取られてるため、

待たされる
待たされる
外部キー制約によるロック
(シャドーロック編)
• シャドーロックとは

クエリが待たされたときなどでも

部分的にロックを獲得する現象のこと
• 注意:私が勝手に呼んでるロック現象です
• 外部キーに関わらず、IN, BETWEENなど

複数行ロックするようなクエリの場合にも発生

(今回は外部キー制約に伴う部分のみを紹介)
13
部分的にロックを取ってしまう原因
InnoDBのINSERTの挙動(簡易)
14
1. テーブルロック確認
2. インデックスを順番に作成
3. 外部キー制約なら共有ロック
4. 他TXからロックの影響確認と

同時にロック(uniq制限チェックなど諸々)
5. インデックス作成完了
待たされるポイント
各々の処理でロックを
確定してしまう
=> シャドーロックになる
=> インデックス定義依存
同じクエリで検証してみる
key Extra
id PRIMARY AUTO-INCR
player KEY-INDEX
item KEY-INDEX
PlayerItem
CREATE TABLE `player_item` (
…
KEY `idx_item` (`item`),
KEY `idx_player` (`player`),
…
);
15
CREATE TABLE `player_item` (
…
KEY `idx_player` (`player`),
KEY `idx_item` (`item`),
…
);
case1
case2
item,playerは外部キー
同じクエリで検証
case1: item -> player のindex順
case2: player -> item のindex順
> BEGIN;
> SELECT * FROM player

WHERE id = 100 FOR UPDATE;
> BEGIN;
トランザクションA トランザクションB
16
case1:item -> player の順でindex定義
> BEGIN;
> SELECT * FROM player

WHERE id = 100 FOR UPDATE;
> BEGIN;
> INSERT INTO player_item

(player,item) VALUES(100,2000);
トランザクションA トランザクションB
17
• Player.id(100)に排他ロックが

取られてるため待たされる
• シャドーロックでitem.id(2000)に

対して共有ロック獲得済み
case1:item -> player の順でindex定義
待たされる
> BEGIN;
> SELECT * FROM player

WHERE id = 100 FOR UPDATE;
> BEGIN;
> INSERT INTO player_item

(player,item) VALUES(100,2000);
トランザクションA トランザクションB
18
• Player.id(100)に排他ロックが

取られてるため待たされる
• シャドーロックでitem.id(2000)に

対して共有ロック獲得済み
case1:item -> player の順でindex定義
待たされる
> SELECT * FROM item

WHERE id = 2000 FOR UPDATE;
DeadLock !!!
> BEGIN;
> SELECT * FROM player

WHERE id = 100 FOR UPDATE;
> BEGIN;
19
case2:player -> item の順でindex定義
トランザクションA トランザクションB
> BEGIN;
> SELECT * FROM player

WHERE id = 100 FOR UPDATE;
> BEGIN;
> INSERT INTO player_item

(player,item) VALUES(100,2000);
20
• Player.id(100)に排他ロックが

取られてるため待たされる
• item.id(2000)に対しては

共有ロックを取ってない

(取る前にPlayer.idで止まった)
case2:player -> item の順でindex定義
トランザクションA トランザクションB
待たされる
> BEGIN;
> SELECT * FROM player

WHERE id = 100 FOR UPDATE;
> BEGIN;
> INSERT INTO player_item

(player,item) VALUES(100,2000);
> SELECT * FROM item

WHERE id = 2000 FOR UPDATE;
21
>
待たされない!
• Player.id(100)に排他ロックが

取られてるため待たされる
• item.id(2000)に対しては

共有ロックを取ってない

(取る前にPlayer.idで止まった)
case2:player -> item の順でindex定義
トランザクションA トランザクションB
待たされる
補足:ロックは食いつく
22
検証
・INSERTでDuplicateEntryになったときの

 ロック獲得状況の確認
ちなみに、DuplicateEntry時など

失敗したら共有ロックになることが知られている

(成功時は排他ロック)
Uniq制限のあるテーブル定義
key Extra
id PRIMARY
token UNIQ-INDEX
item KEY-INDEX
PlayerToken
外部キー制約する側(子)
23
CREATE TABLE `player_token` (
…
PRIMARY KEY (`id`),
UNIQUE KEY `idx_token` (`token`),
KEY `idx_item` (`item`),
…
);
idがplayer.idの外部キー
itemがitem.idの外部キー
id -> token -> itemのindex順
> BEGIN;
> INSERT INTO player_token
(id,item,token) VALUES
(100,1000, ABCD );
> BEGIN;
トランザクションA
24
トランザクションB
INSERTでDuplicateEntryになったとき
>
> BEGIN;
> INSERT INTO player_token
(id,item,token) VALUES
(100,1000, ABCD );
> BEGIN;
> INSERT INTO player_token

(id,item,token) VALUES
(200,2000, ABCD );
トランザクションA
25
トランザクションB
シャドーロックでPlayer.id(200)

の共有ロックは獲得済み。

Item.id(2000)の前にtokenの

uniq制限でひっかかる
INSERTでDuplicateEntryになったとき
待たされる
>
id -> token -> itemのindex順
> BEGIN;
> INSERT INTO player_token
(id,item,token) VALUES
(100,2000, ABCD );
> BEGIN;
> INSERT INTO player_token

(id,item,token) VALUES
(200,2000, ABCD );
> COMMIT;
トランザクションA
26
トランザクションB
> (Duplicate Entry )
>
DuplicateEntryになったものの、

トランザクションが解除されたわけ

ではない。Player.id(200)の
共有ロックは獲得済み
INSERTでDuplicateEntryになったとき
id -> token -> itemのindex順
> BEGIN;
> INSERT INTO player_token
(id,item,token) VALUES
(100,2000, ABCD );
> BEGIN;
> INSERT INTO player_token

(id,item,token) VALUES
(200,2000, ABCD );
> COMMIT;
トランザクションA, A
27
トランザクションB
> BEGIN;
> (Duplicate Entry )
>
DuplicateEntryになったものの、

トランザクションが解除されたわけ

ではない。Player.id(200)の
共有ロックは獲得済み
INSERTでDuplicateEntryになったとき
> SELECT * FROM item
WHERE id = 2000 FOR UPDATE;
id -> token -> itemのindex順
> BEGIN;
> INSERT INTO player_token
(id,item,token) VALUES
(100,2000, ABCD );
> BEGIN;
> INSERT INTO player_token

(id,item,token) VALUES
(200,2000, ABCD );
> COMMIT;
トランザクションA, A
28
トランザクションB
> BEGIN;
> SELECT * FROM player
WHERE id = 200 FOR UPDATE;
> (Duplicate Entry )
>
DuplicateEntryになったものの、

トランザクションが解除されたわけ

ではない。Player.id(200)の
共有ロックは獲得済み
INSERTでDuplicateEntryになったとき
> SELECT * FROM item
WHERE id = 2000 FOR UPDATE;
待たされる
id -> token -> itemのindex順
まとめ
• 共有->排他のロック順はデッドロックの原因
• 外部キー制約があるとINSERT時に親に共有ロック
• クエリが待たされてる状態でも

部分的にロックは獲得される(シャドーロック)
• 外部キー制約の共有ロック順序はテーブル定義依存
• 外部キー制約を付けるならINSERT前に排他ロック
29

More Related Content

What's hot

SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)Takuto Wada
 
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」Takuto Wada
 
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考えるGoのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考えるpospome
 
それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?Yoshitaka Kawashima
 
強いて言えば「集約どう実装するのかな、を考える」な話
強いて言えば「集約どう実装するのかな、を考える」な話強いて言えば「集約どう実装するのかな、を考える」な話
強いて言えば「集約どう実装するのかな、を考える」な話Yoshitaka Kawashima
 
世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture世界一わかりやすいClean Architecture
世界一わかりやすいClean ArchitectureAtsushi Nakamura
 
PostgreSQLアンチパターン
PostgreSQLアンチパターンPostgreSQLアンチパターン
PostgreSQLアンチパターンSoudai Sone
 
イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)Yoshitaka Kawashima
 
ドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみようドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみよう増田 亨
 
Akkaとは。アクターモデル とは。
Akkaとは。アクターモデル とは。Akkaとは。アクターモデル とは。
Akkaとは。アクターモデル とは。Kenjiro Kubota
 
イミュータブルデータモデルの極意
イミュータブルデータモデルの極意イミュータブルデータモデルの極意
イミュータブルデータモデルの極意Yoshitaka Kawashima
 
シリコンバレーの「何が」凄いのか
シリコンバレーの「何が」凄いのかシリコンバレーの「何が」凄いのか
シリコンバレーの「何が」凄いのかAtsushi Nakada
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Kohei Tokunaga
 
DDDのモデリングとは何なのか、 そしてどうコードに落とすのか
DDDのモデリングとは何なのか、 そしてどうコードに落とすのかDDDのモデリングとは何なのか、 そしてどうコードに落とすのか
DDDのモデリングとは何なのか、 そしてどうコードに落とすのかKoichiro Matsuoka
 
やはりお前らのMVCは間違っている
やはりお前らのMVCは間違っているやはりお前らのMVCは間違っている
やはりお前らのMVCは間違っているKoichi Tanaka
 
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)Mikiya Okuno
 
Python におけるドメイン駆動設計(戦術面)の勘どころ
Python におけるドメイン駆動設計(戦術面)の勘どころPython におけるドメイン駆動設計(戦術面)の勘どころ
Python におけるドメイン駆動設計(戦術面)の勘どころJunya Hayashi
 
TDD のこころ @ OSH2014
TDD のこころ @ OSH2014TDD のこころ @ OSH2014
TDD のこころ @ OSH2014Takuto Wada
 

What's hot (20)

SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
 
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
 
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考えるGoのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
 
それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?
 
ヤフー社内でやってるMySQLチューニングセミナー大公開
ヤフー社内でやってるMySQLチューニングセミナー大公開ヤフー社内でやってるMySQLチューニングセミナー大公開
ヤフー社内でやってるMySQLチューニングセミナー大公開
 
強いて言えば「集約どう実装するのかな、を考える」な話
強いて言えば「集約どう実装するのかな、を考える」な話強いて言えば「集約どう実装するのかな、を考える」な話
強いて言えば「集約どう実装するのかな、を考える」な話
 
世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture
 
PostgreSQLアンチパターン
PostgreSQLアンチパターンPostgreSQLアンチパターン
PostgreSQLアンチパターン
 
イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)
 
ドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみようドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみよう
 
Akkaとは。アクターモデル とは。
Akkaとは。アクターモデル とは。Akkaとは。アクターモデル とは。
Akkaとは。アクターモデル とは。
 
Tackling Complexity
Tackling ComplexityTackling Complexity
Tackling Complexity
 
イミュータブルデータモデルの極意
イミュータブルデータモデルの極意イミュータブルデータモデルの極意
イミュータブルデータモデルの極意
 
シリコンバレーの「何が」凄いのか
シリコンバレーの「何が」凄いのかシリコンバレーの「何が」凄いのか
シリコンバレーの「何が」凄いのか
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行
 
DDDのモデリングとは何なのか、 そしてどうコードに落とすのか
DDDのモデリングとは何なのか、 そしてどうコードに落とすのかDDDのモデリングとは何なのか、 そしてどうコードに落とすのか
DDDのモデリングとは何なのか、 そしてどうコードに落とすのか
 
やはりお前らのMVCは間違っている
やはりお前らのMVCは間違っているやはりお前らのMVCは間違っている
やはりお前らのMVCは間違っている
 
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)
 
Python におけるドメイン駆動設計(戦術面)の勘どころ
Python におけるドメイン駆動設計(戦術面)の勘どころPython におけるドメイン駆動設計(戦術面)の勘どころ
Python におけるドメイン駆動設計(戦術面)の勘どころ
 
TDD のこころ @ OSH2014
TDD のこころ @ OSH2014TDD のこころ @ OSH2014
TDD のこころ @ OSH2014
 

外部キー制約に伴うロックの小話