SlideShare a Scribd company logo
なかったらINSERTしたい
し、あるならロック取りたい
やん?
第2回 DevOps勉強会
ichirin2501
1
似たネタで乗っかることにしました
2
想定する状況(MySQL)
• ユーザーアクションでINSERTしたいんだよね
• 既にデータがあるなら排他制御しつつ参照して

処理したいんだよね
=> 罠がある(InnoDB, REPEATABLE-READ)
なぜだめなのか、に焦点をあてて話したいと思います
用意したコードはてきとーなので注意
3
よくある1
$dbh->do( BEGIN );
$row = $dbh->do( SELECT FOR UPDATE );
if (! $row) {
$dbh->do( INSERT );
}
4
だめな理由
• 空打ちロックはギャップロックになる
• ギャップロックされた空間のINSERTは

全てブロックされる
• そしてギャップロック同士はブロックされない
5
ギャップロックって?
インデックスの 間をロックすること
5
6
10
id (pk)
[-inf, 5)
[7, 10)
[11, +inf]
6
ギャップロックされた

空間のINSERTは止まる
5
6
10
id (pk)
tx A tx B
SELECT * FROM t 

WHERE id = 4 FOR UPDATE
BEGIN BEGIN
INSERT INTO t (id)
VALUES (1);
ブロックされる
id=2,3,4も同様にブロック

id=7とかはブロックされない
7
ギャップロック同士は

ブロックしない
5
6
10
id (pk)
tx A tx B
SELECT * FROM t 

WHERE id = 4 FOR UPDATE
BEGIN BEGIN
SELECT * FROM t 

WHERE id = 3 FOR UPDATE
INSERT INTO t (id) VALUES(4)
INSERT INTO t (id) VALUES(3)
同じギャップ空間だけど止まらない
Deadlock Error
8
ちなみに
5
6
10
id (pk)
tx A tx B
BEGIN BEGIN
INSERT INTO t (id) VALUES(4)
INSERT INTO t (id) VALUES(3)
これはデッドロックにならない
9
よくある2 - とりあえず挿入
$dbh->do( BEGIN );
$row;
try {
$dbh->do( INSERT );
} catch {
$row = $dbh->do( SELECT FOR UPDATE );
};
10
だめな理由
• INSERTでDuplicate-Entryになったら、

共有ロックになる
• 共有 -> 排他ロックはデッドロックの原因となり、

Dup -> FOR-UPDATEの 間に刺さる
• Dupでもロックを取るので大量発行してると

ロック待ちで詰む
11
共有 -> 排他ロック
5
6
10
id (pk)
tx A tx B
SELECT * FROM t 

WHERE id = 5 FOR UPDATE
BEGIN BEGIN
INSERT INTO t (id) VALUES (5)
SELECT * FROM t 

WHERE id = 5 FOR UPDATE
Deadlock Error
Err: Duplicate Entry…
12
だったらIGNORE?
$dbh->do( BEGIN );
$res = $dbh->do( INSERT IGNORE );
if (!$res) {
$row = $dbh->do( SELECT FOR UPDATE );
};
マサカリ投げていく所存
無視しても共有ロックは取られる
13
よくある3 - ロックなし参照
$dbh->do( BEGIN );
$row = $dbh->do( SELECT );
if (! $row) {
$dbh->do( INSERT );
} else {
$row = $dbh->do( SELECT FOR UPDATE );
}
14
あまりよくない理由
• SELECTからINSERTまでの 間でDuplicateEntry
• 最初のSELECT文で全体のスナップショットが取ら
れるため、排他制御としては不十分な状態になる
15
面倒だけど無難な解決案
• Duplicate-EntryになったらRollbackして

リトライする
16
バッドノウハウのご紹介
$dbh->do( BEGIN );
$dbh->do( INSERT ON DUPLICATE KEY UPDATE );
$row = $dbh->do( SELECT FOR UPDATE );
ON DUPLICATE KEY UPDATEで
無意味な更新をするのがミソ

(name = name みたいな
17
解決されること
• INSERTでもUPDATEでも排他ロックになるため、

共有 -> 排他ロックのデッドロックが発生しない
• トランザクション開始直後に打てばスナップショッ
トによるバグ埋め込みが発生しない
18
そんな感じで、

ロックと仲良くしよう!
19

More Related Content

なかったらINSERTしたいし、あるならロック取りたいやん?

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy