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

マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!mosa siru
 
ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計Yoshinori Matsunobu
 
初心者向けMongoDBのキホン!
初心者向けMongoDBのキホン!初心者向けMongoDBのキホン!
初心者向けMongoDBのキホン!Tetsutaro Watanabe
 
あなたの知らないPostgreSQL監視の世界
あなたの知らないPostgreSQL監視の世界あなたの知らないPostgreSQL監視の世界
あなたの知らないPostgreSQL監視の世界Yoshinori Nakanishi
 
今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -
今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -
今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -onozaty
 
Linux女子部 systemd徹底入門
Linux女子部 systemd徹底入門Linux女子部 systemd徹底入門
Linux女子部 systemd徹底入門Etsuji Nakai
 
なかったらINSERTしたいし、あるならロック取りたいやん?
なかったらINSERTしたいし、あるならロック取りたいやん?なかったらINSERTしたいし、あるならロック取りたいやん?
なかったらINSERTしたいし、あるならロック取りたいやん?ichirin2501
 
MySQL・PostgreSQLだけで作る高速あいまい全文検索システム
MySQL・PostgreSQLだけで作る高速あいまい全文検索システムMySQL・PostgreSQLだけで作る高速あいまい全文検索システム
MySQL・PostgreSQLだけで作る高速あいまい全文検索システムKouhei Sutou
 
Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編Masahito Zembutsu
 
NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした
NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くしたNginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした
NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くしたtoshi_pp
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法についてYuji Otani
 
PostgreSQLをKubernetes上で活用するためのOperator紹介!(Cloud Native Database Meetup #3 発表資料)
PostgreSQLをKubernetes上で活用するためのOperator紹介!(Cloud Native Database Meetup #3 発表資料)PostgreSQLをKubernetes上で活用するためのOperator紹介!(Cloud Native Database Meetup #3 発表資料)
PostgreSQLをKubernetes上で活用するためのOperator紹介!(Cloud Native Database Meetup #3 発表資料)NTT DATA Technology & Innovation
 
DockerとPodmanの比較
DockerとPodmanの比較DockerとPodmanの比較
DockerとPodmanの比較Akihiro Suda
 
怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション土岐 孝平
 
雑なMySQLパフォーマンスチューニング
雑なMySQLパフォーマンスチューニング雑なMySQLパフォーマンスチューニング
雑なMySQLパフォーマンスチューニングyoku0825
 
Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】
Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】
Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】Masahito Zembutsu
 
Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方Taku Miyakawa
 
PostgreSQL 15の新機能を徹底解説
PostgreSQL 15の新機能を徹底解説PostgreSQL 15の新機能を徹底解説
PostgreSQL 15の新機能を徹底解説Masahiko Sawada
 
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)NTT DATA Technology & Innovation
 

What's hot (20)

マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!
 
ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計
 
初心者向けMongoDBのキホン!
初心者向けMongoDBのキホン!初心者向けMongoDBのキホン!
初心者向けMongoDBのキホン!
 
あなたの知らないPostgreSQL監視の世界
あなたの知らないPostgreSQL監視の世界あなたの知らないPostgreSQL監視の世界
あなたの知らないPostgreSQL監視の世界
 
今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -
今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -
今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -
 
Linux女子部 systemd徹底入門
Linux女子部 systemd徹底入門Linux女子部 systemd徹底入門
Linux女子部 systemd徹底入門
 
なかったらINSERTしたいし、あるならロック取りたいやん?
なかったらINSERTしたいし、あるならロック取りたいやん?なかったらINSERTしたいし、あるならロック取りたいやん?
なかったらINSERTしたいし、あるならロック取りたいやん?
 
MySQL・PostgreSQLだけで作る高速あいまい全文検索システム
MySQL・PostgreSQLだけで作る高速あいまい全文検索システムMySQL・PostgreSQLだけで作る高速あいまい全文検索システム
MySQL・PostgreSQLだけで作る高速あいまい全文検索システム
 
Metaspace
MetaspaceMetaspace
Metaspace
 
Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編
 
NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした
NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くしたNginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした
NginxとLuaを用いた動的なリバースプロキシでデプロイを 100 倍速くした
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法について
 
PostgreSQLをKubernetes上で活用するためのOperator紹介!(Cloud Native Database Meetup #3 発表資料)
PostgreSQLをKubernetes上で活用するためのOperator紹介!(Cloud Native Database Meetup #3 発表資料)PostgreSQLをKubernetes上で活用するためのOperator紹介!(Cloud Native Database Meetup #3 発表資料)
PostgreSQLをKubernetes上で活用するためのOperator紹介!(Cloud Native Database Meetup #3 発表資料)
 
DockerとPodmanの比較
DockerとPodmanの比較DockerとPodmanの比較
DockerとPodmanの比較
 
怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション
 
雑なMySQLパフォーマンスチューニング
雑なMySQLパフォーマンスチューニング雑なMySQLパフォーマンスチューニング
雑なMySQLパフォーマンスチューニング
 
Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】
Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】
Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】
 
Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方
 
PostgreSQL 15の新機能を徹底解説
PostgreSQL 15の新機能を徹底解説PostgreSQL 15の新機能を徹底解説
PostgreSQL 15の新機能を徹底解説
 
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
今こそ知りたいSpring Batch(Spring Fest 2020講演資料)
 

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