ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transactionについて

Mysqlでタイトルのようなエラーが出てドキッとすることがあった。

原因としては、MySQLのセッションがトランザクションを使用している時に、ロックがかかった状態のまま、他のセッションがそのテーブルに対して更新しようとして、timeoutが発生している事らしい。


他のセッションによりロックが長時間保持されていること

timeoutまでの時間の確認方法

mysql> show global variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 50    | 
+--------------------------+-------+
1 row in set (0.00 sec)
結論から書くと・・

インデックス、ユニークキー制約のないカラムに対して、トランザクションを貼ったまま放置すると、行ロックではなくてテーブル・ロックがかかってしまうので気をつけようという話です。

テストしてみた。

用意したテーブル

mysql> show create table lock_test;
+-----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table     | Create Table                                                                                                                                                                         |
+-----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| lock_test | CREATE TABLE `lock_test` (
  `id` int(11) NOT NULL default '0',
  `c1` int(11) default NULL,
  `c2` int(11) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 | 
+-----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)


用意したデータ

insert into lock_test (id, c1, c2) values (1, 10,10);
insert into lock_test (id, c1, c2) values (2, 20,20);

状態の確認

mysql> select * from lock_test;
+----+------+------+
| id | c1   | c2   |
+----+------+------+
|  1 |   10 |   10 | 
|  2 |   20 |   20 | 
+----+------+------+
2 rows in set (0.00 sec)

実験1(primaryキーがあるカラム)

【セッション1】

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

mysql> update lock_test set c2 = 100 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

【セッション2】

mysql> update lock_test set c2 = 100 where id = 2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0


実験2(インデックス貼ってないカラム)

【セッション1】

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

mysql> update lock_test set c2 = 100 where c1 = 10;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

【セッション2】

mysql> update lock_test set c2 = 200 where c1 = 20;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

実験3(c2にインデックスを貼って見る)
【セッション1】

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

mysql> update lock_test set c2 = 100 where c1 = 10;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

【セッション2】

mysql> update lock_test set c2 = 200 where c1 = 20;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

実験3【ユニークキー制約をはってみる】

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

mysql> update lock_test set c2 = 100 where c1 = 10;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

【セッション2】

mysql> update lock_test set c2 = 200 where c1 = 20;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0