空のUPDATEによるDEADLOCK
概要
負荷検証中に deadlock のログを見つけて、その対処をしたのでメモです。
クエリ特定
SHOW ENGINE INNODB STATUS;
コマンドを実行すると、
LATEST DETECTED DEADLOCK
でどのクエリで deadlock が発生したのかわかります。
今回は
INSERT INTO tokens (id, token) VALUES(1, 'token'); INSERT INTO tokens (id, token) VALUES(2, 'token2');
のようなクエリで deadlock になっていることがわかりました。
原因&対処
INSERT の直前で、UPDATE tokens SET delete_time=NOW() WHERE id=1;
のような UPDATE をしており、
これが空の場合、ネクストキーロックがかかってしまうためとわかりました。
参考
試す。 ターミナル1
mysql> BEGIN; Query OK, 0 rows affected (0.01 sec) mysql> UPDATE tokens SET delete_time=NOW() WHERE id=1; Query OK, 0 rows affected (0.03 sec) Rows matched: 0 Changed: 0 Warnings: 0 mysql> INSERT INTO tokens (id, token) VALUES(1, 'token'); Query OK, 1 row affected (7.07 sec)
ターミナル2
mysql> BEGIN; Query OK, 0 rows affected (0.00 sec) mysql> UPDATE tokens SET delete_time=NOW() WHERE id=2; Query OK, 0 rows affected (0.01 sec) Rows matched: 0 Changed: 0 Warnings: 0 mysql> INSERT INTO tokens (id, token) VALUES(2, 'token2'); ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
Deadlock!!!
対処。
アプリケーションのロジックで、まず、SELECT で存在をチェックして、存在すれば UPDATE する形にしました。
// ロックを避けるために存在チェック isExist, err := // SELECT * FROM tokens WHERE id=?; if err != nil { return nil, err } if isExist { // UPDATE } // INSERT
XXXロックとかの理解を深めないと。。。