地方エンジニアの学習日記

興味ある技術の雑なメモだったりを書いてくブログ。たまに日記とガジェット紹介。

【MySQL】ギャップロックはデッドロックをするかもしれない

InnoDB のギャップロックは、「単に抑制的」です。つまり、ほかのトランザクションによるギャップへの挿入が停止されるだけです。したがって、ギャップ X ロックの効果はギャップ S ロックと同じです。

dev.mysql.com

タイトル通りそのまま。SELECT ~ FOR UPDATEが空振るとギャップロックを取るが例えば2トランザクションで同時に発生した場合にはどちらもロック待ちは発生せずに先に進めてしまう。先に進めた状態でどちらも排他ロックを取ろうとするとデッドロックを起こすという。

テーブル

CREATE TABLE `t1` (
`col_pk` int(11) NOT NULL AUTO_INCREMENT,
`col2` int(11) DEFAULT NULL,
`col3` varchar(10) DEFAULT NULL,
PRIMARY KEY (`col_pk`),
KEY `col2` (`col2`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

トランザクションA

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t2 where col_pk = 8 for update;
Empty set (0.00 sec)

mysql> insert into t2 values(9,10,100);
Query OK, 1 row affected (6.59 sec)

トランザクションB

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t2 where col_pk = 8 for update;
Empty set (0.00 sec)

mysql> insert into t2 values(9,10,100);
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

どうすりゃいいの?

空振らないように実装する(トレードオフあり)、アプリでリトライできるならリトライ。あとは分離レベルを変えてしまうのもファントムリードが発生しないので良さそう。

zenn.dev