InnoDB のギャップロックは、「単に抑制的」です。つまり、ほかのトランザクションによるギャップへの挿入が停止されるだけです。したがって、ギャップ X ロックの効果はギャップ S ロックと同じです。
タイトル通りそのまま。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;
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)
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
どうすりゃいいの?
空振らないように実装する(トレードオフあり)、アプリでリトライできるならリトライ。あとは分離レベルを変えてしまうのもファントムリードが発生しないので良さそう。