MySQL 12 min read

MySQLのERROR 1205「Lock wait timeout」 ― 止めている接続の探し方と、巻き戻らないトランザクション

※この記事にはアフィリエイト広告(プロモーション)が含まれます。

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction は、InnoDB の行ロックを innodb_lock_wait_timeout(既定 50 秒)待っても取れなかったときのエラーです。この記事の出力は MySQL 8.4.11(Ubuntu 26.04)で採りました。

まず見る(結論)

SELECT waiting_pid, waiting_query, blocking_pid, blocking_query,
       blocking_trx_age, sql_kill_blocking_connection
  FROM sys.innodb_lock_waits;
  • blocking_query が NULL なら、止めている側はコミットせずに放置されている。KILL QUERY では外れず、KILL <blocking_pid> で接続ごと切る
  • 1205 で取り消されるのはその文だけ。トランザクションは続いているので、アプリ側で ROLLBACK する

止めている接続を探す

待たされている間に sys.innodb_lock_waits を引くと、待っている側と止めている側が1行で出ます(抜粋)。

mysql> SELECT * FROM sys.innodb_lock_waits\G
*************************** 1. row ***************************
                wait_started: 2026-11-10 14:46:43
               wait_age_secs: 2
                locked_table: `fsv`.`acct`
                locked_index: PRIMARY
                 waiting_pid: 13
               waiting_query: UPDATE acct SET bal=bal+10 WHERE id=1
           waiting_lock_mode: X,REC_NOT_GAP
                blocking_pid: 11
              blocking_query: DO SLEEP(30)
        blocking_trx_started: 2026-11-10 14:46:38
            blocking_trx_age: 00:00:07
     sql_kill_blocking_query: KILL QUERY 11
sql_kill_blocking_connection: KILL 11

blocking_pid は SHOW PROCESSLIST の Id と同じ番号です。行ロックの中身まで見るなら performance_schema.data_locks を使います。LOCK_STATUS が WAITING の行が待っている側で、LOCK_DATA が対象の主キーの値(ここでは 1)です。

Sleep のまま握っている接続

BEGIN → UPDATE のあとコミットせずに止まった接続は、SHOW PROCESSLIST では Command が Sleep、Info が NULL です。何もしていないように見えますが、ロックは持ったままです。

mysql> SELECT trx_mysql_thread_id, trx_state, trx_started, trx_query FROM information_schema.innodb_trx;
+---------------------+-----------+---------------------+----------------------------------+
| trx_mysql_thread_id | trx_state | trx_started         | trx_query                        |
+---------------------+-----------+---------------------+----------------------------------+
|                  33 | LOCK WAIT | 2026-11-10 14:47:46 | UPDATE acct SET bal=1 WHERE id=3 |
|                  32 | RUNNING   | 2026-11-10 14:47:44 | NULL                             |
+---------------------+-----------+---------------------+----------------------------------+

trx_query が NULL で trx_started の古い行が、放置された接続です。

止めている側(32)に実行 ロック
KILL QUERY 32 外れない。sys.innodb_lock_waits に同じ行が残る
KILL 32 外れる。32 の UPDATE はロールバックされ、33 の UPDATE が通った

KILL QUERY は実行中の文を止めるだけで、文を実行していない接続には効きません。接続を切るには sys.innodb_lock_waits の sql_kill_blocking_connection 列の文を使います。

1205 のあともトランザクションは続く

同じトランザクションの中で INSERT したあと UPDATE が 1205 になり、そのまま次の INSERT と COMMIT を実行しました。

BEGIN;
INSERT INTO log1(msg) VALUES ('before');
UPDATE acct SET bal=bal+10 WHERE id=2;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
SELECT trx_state FROM information_schema.innodb_trx WHERE trx_mysql_thread_id = CONNECTION_ID();
-- RUNNING
INSERT INTO log1(msg) VALUES ('after');
COMMIT;

COMMIT 後の log1 には before と after の2行が入り、UPDATE だけが反映されていませんでした。innodb_rollback_on_timeout が既定の OFF だと、1205 で取り消されるのは待っていた文だけです。

1205 を受けたら ROLLBACK してからトランザクションをやり直します。innodb_rollback_on_timeout は実行中に変えられません(SET GLOBAL は ERROR 1238 ... read only variable)。設定ファイルに書いて再起動する必要があります。

違う行なのに待たされる(索引の無い列)

UPDATE acct SET ... WHERE name = 'c'(name に索引なし)をコミット前で止め、別の接続から id=5 の行を UPDATE しました。

name の索引 止めている側のレコードロック数 id=5 の UPDATE
なし 6(5行の表) 1205
あり 3 通る

索引の無い列で絞った UPDATE / DELETE は、全行を読みながらロックを取ります。関係のない行の更新まで待たされるときは、WHERE の列に索引があるかを EXPLAIN で確かめます。

ALTER TABLE でも 1205 が出る

開いたままのトランザクションが表を SELECT しているときに ALTER TABLE を実行すると、メタデータロックを待ちます。こちらの上限は innodb_lock_wait_timeout ではなく lock_wait_timeout(既定 31536000 秒=1年)です。

mysql> SET SESSION lock_wait_timeout = 3;
mysql> ALTER TABLE acct ADD COLUMN note VARCHAR(10);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

エラー番号とメッセージは行ロックのときと同じなので、1205 が ALTER TABLE などの DDL で出たらメタデータロックを疑います。待っている間の見え方は ALTER TABLE の記事に書きました。

待ち時間を変える

SET SESSION innodb_lock_wait_timeout = 10;   -- この接続だけ
SET GLOBAL  innodb_lock_wait_timeout = 10;

SET GLOBAL で 7 にしても、その接続の @@SESSION は 50 のままでした。7 になったのは新しく接続したセッションだけです。接続プールを使っているアプリは、プールの接続を作り直すか、接続時に SET SESSION を実行するように設定します。

1205 はエラーログ(/var/log/mysql/error.log)には残りませんでした。あとから追うには、アプリ側でエラーを記録するか、待っている間に sys.innodb_lock_waits を定期的に保存します。デッドロック(ERROR 1213)は待たずにすぐ返るエラーで、別の記事で扱っています。

関連: MySQLのERROR 1213「Deadlock found」 / MySQLのALTER TABLE / MySQLのEXPLAINの見方 / PostgreSQLのロック待ちを調べる

MySQL をもっと深く知るための本

MySQL徹底入門 第5版 MySQL 8.4 LTS対応

翔泳社・2025年6月・MySQL 8.4 LTS 対応

MySQL 8.4 に対応した入門書。日本MySQLユーザ会のメンバーが執筆している。

Amazonで見る

MySQL運用・管理[実践]入門 〜安全かつ高速にデータを扱う内部構造・動作原理を学ぶ

技術評論社・2024年5月

内部構造と動作原理から、運用・管理の考え方を学ぶ。

Amazonで見る

Amazonのアソシエイトとして、FIRE SE は適格販売により収入を得ています。