InnoDBにはデッドロック検出機能があると言われていますが、これがいまひとつどういう条件で発動するのかがよくわかっていません。
http://dev.mysql.com/doc/refman/5.5/en/innodb-deadlock-detection.html
とくに、SHOW ENGINE INNODB STATUS
のLATEST DETECTED DEADLOCK
セクションをみたときに、transaction (1), (2)ともにACTIVE 0 secで、まったく待ち時間なく、瞬時にデッドロックと判定されていることがあります。
この、デッドロックと判定される条件とは、具体的にどういうものでしょうか?
具体例を出します。今回発生してる例では、同じUNIQUE INDEX (a, b, c)上の全く同一のタプル(1, 2, 3)に対して
SELECT ... WHERE a=1 and b=2 and c=3 FOR UPDATE
と
INSERT INTO ... (a, b, c, v) VALUES (1,2,3,1) ON DUPLICATE KEY UPDATE v = v + 1
がコンフリクトして、前者がロールバックされています。コード的には、単純化していうと
START TRANSACTION
SELECT ... WHERE a=1 and b=2 and c=3 FOR UPDATE
INSERT INTO ... (a, b, c, v) VALUES (1,2,3,1) ON DUPLICATE KEY UPDATE v = v + 1
COMMIT
が並列に走るイメージです。
期待した挙動は、SELECT ... FOR UPDATE
に入った時点で、すでに稼働中のINSERT ... ON DUPLICATE KEY UPDATE
が同じタプルに対して存在していたら、それが終わるまで(ロックの解放を)待つ、というものでしたが、そのようには動いてくれず、デッドロックになってしまうようです。