MySQLのレコードロックはギャップロックにはならんよね、と確かめた話
また対象読者が非常に限られる話を書きますが、気になって調べたのでメモ
MySQLでは INSERT ... ON DUPLICATE KEY UPDATE
という構文があってこれはINSERTをしてKEYが重複したらUPDATEするという挙動をします。
で、MySQLのマニュアルを見ると共有ロックじゃなくて排他ロックをかけるよ、とか PRIMARY KEY の DUPLICATE には インデックスレコードロックをかけて、UNIQUE KEY の DUPLICATEにはネクストキーロックをかけるよ、と書いてあります。
INSERT ... ON DUPLICATE KEY UPDATE
differs from a simpleINSERT
in that an exclusive lock rather than a shared lock is placed on the row to be updated when a duplicate-key error occurs. An exclusive index-record lock is taken for a duplicate primary key value. An exclusive next-key lock is taken for a duplicate unique key value.MySQL :: MySQL 5.7 Reference Manual :: 14.5.3 Locks Set by Different SQL Statements in InnoDB
ネクストキーロックってギャップロックになるので割と困るんですよね。トランザクションが終わるまで、そのギャップの間にはレコードを入れられなくなるので。
というところまでわかっていたのですが、インデックスレコードロックはギャップロックにはならないかどうかあんまり自信がないのにコードレビューで断言して出てきたので本当かなあ、と調べたくなったという話。
で、さっそく調べたのですが
PRIMARY KEY が DUPLICATE した場合 → PRIMARY インデックスのみレコードロックする(ギャップロック無し)。UNIQUE インデックスはレコードロックする(ギャップロック無し)
UNIQUE KEY が DUPLICATE した場合 → PRIMARY インデックスはレコードロックする(ギャップロック無し)。UNIQUE インデックスはネクストキーロックする(もちろんギャップロックあり)
という挙動になりました。UNIQUE インデックスの挙動がキモい。。。
MySQLのリファレンスマニュアル言っていることは正しいけど説明不足では!!!(じゃあ連絡しろ、という話ではある)
こういう時にMySQLのコード見てここが問題だ!って言えるとカッコいいんですが、そんなにMySQL詳しくない罠
gist9923b5edecf5a0abf4aed846368f70af