2026年5月20日
2026年5月20日
MySQLのテーブルロックを解除する方法(原因の特定とKILL)
MySQL で、ある処理がテーブルのロックを握ったまま終わらないと、同じテーブルを使うほかのクエリが待たされます。WordPress では、ページの表示が止まる、管理画面の保存が終わらない、待ちの接続がたまって接続数の上限に達する、といった形で表に出ます。
ロックを「解除する」とは、実際にはロックを握っている接続(セッション)を見つけて、その処理を終わらせることです。この記事では、MySQL 8.0 / 8.4 を前提に、ロック待ちの確認、原因の接続の特定、KILL による解除、再発の防ぎ方を説明します(2026年10月時点)。MariaDB でも SHOW PROCESSLIST と KILL は同じように使えますが、sys スキーマや Performance Schema の既定の設定が異なります。
ロックの種類と、よく出る状態
| ロックの種類 | 主な原因 | SHOW PROCESSLIST の State の例 |
|---|---|---|
| メタデータロック | 終わっていないトランザクションがあるテーブルへの ALTER TABLE、LOCK TABLES |
Waiting for table metadata lock |
| テーブルレベルロック | MyISAM のテーブルへの書き込み、LOCK TABLES |
Waiting for table level lock |
| InnoDB の行ロック | 同じ行を更新するトランザクションの競合、コミットされないまま残ったトランザクション | 待っている側は updating など(ロック待ちとは表示されない) |
InnoDB の行ロック待ちは、State を見ただけではわかりません。後述の sys.innodb_lock_waits で確認します。
手順1: 実行中の処理を一覧する
MySQL に接続し、実行中の処理を確認します。
SHOW FULL PROCESSLIST;
表示例です(一部の列を省略)。
Id User db Command Time State Info
10 wp wp Query 7 User sleep select sleep(60)
11 wp wp Query 5 updating update t set v=11 where id=1
12 wp wp Query 4 Waiting for table metadata lock alter table t add column c int
Idは接続の番号です。あとでKILLに使います。Timeは今の状態になってからの秒数です。長く続いている処理が手がかりになります。CommandがSleepの接続でも、トランザクションを開いたまま止まっていると、ロックを握り続けていることがあります。
ほかのユーザーの接続まで見るには PROCESS 権限が必要です。権限がない場合は、自分のユーザーの接続だけが表示されます。
手順2: ロックを握っている接続を特定する
InnoDB の行ロック待ち
MySQL 8.0 以降では、sys スキーマのビューで「誰が誰を待たせているか」を一度に確認できます。
SELECT waiting_pid, waiting_query, blocking_pid, blocking_query,
sql_kill_blocking_connection
FROM sys.innodb_lock_waits\G
waiting_pid: 11
waiting_query: update t set v=11 where id=1
blocking_pid: 10
blocking_query: select sleep(60)
sql_kill_blocking_connection: KILL 10
blocking_pid がロックを握っている接続の Id です。sql_kill_blocking_connection には、その接続を終わらせるための KILL 文がそのまま入っています。blocking_query は「今実行している文」なので、ロックを取った文とは限りません(上の例では、行を更新したあとのトランザクションで別の文を実行しています)。
メタデータロック待ち(ALTER TABLE や LOCK TABLES)
SELECT object_schema, object_name, waiting_pid, waiting_query,
blocking_pid, blocking_lock_type, sql_kill_blocking_connection
FROM sys.schema_table_lock_waits
WHERE waiting_pid <> blocking_pid\G
このビューは、待っている接続が自分自身を待っているように見える行も返すため、waiting_pid <> blocking_pid で除いています。ALTER TABLE が待たされている場合、終わっていないトランザクションを持つ接続が blocking_pid に出ます。このビューは Performance Schema のメタデータロックの計測を使います。MySQL 8.0 以降は既定で有効です。
長く開いたままのトランザクション
SELECT trx_mysql_thread_id, trx_started, trx_state, trx_rows_locked, trx_query
FROM information_schema.INNODB_TRX
ORDER BY trx_started;
trx_started が古いトランザクションが、ロックを握ったままになっている候補です。trx_mysql_thread_id が SHOW PROCESSLIST の Id に当たります。trx_state が LOCK WAIT のものは、待たされている側です。
LOCK TABLES や MyISAM のテーブル
SHOW OPEN TABLES WHERE In_use > 0;
In_use は、そのテーブルに対するテーブルロックとロック要求の数です。0 より大きいテーブルが、今ロックされているか、ロックを待たれています。どの接続が握っているかまではわからないため、上の sys.schema_table_lock_waits や SHOW PROCESSLIST とあわせて見ます。
手順3: KILL でロックを解除する
ロックを握っている接続がわかったら、その接続を終わらせます。
-- 接続ごと終わらせる(ロックが解放される)
KILL 10;
-- 実行中の文だけを止め、接続は残す
KILL QUERY 10;
KILL(KILL CONNECTIONと同じ)は、接続を切断します。InnoDB のコミットされていない変更は取り消され(ロールバック)、その接続が持っていたロックはすべて解放されます。KILL QUERYは実行中の文を止めるだけで、接続とトランザクションは残ります。トランザクションを開いたまま止まっている接続のロックは、KILL QUERYでは解放されません。KILLはすぐに戻りますが、大きなトランザクションの取り消しには時間がかかることがあります。取り消しが終わるまでロックは残ります。- ほかのユーザーの接続を止めるには
CONNECTION_ADMIN権限(または非推奨のSUPER)が必要です。レンタルサーバーの共用データベースでは、自分のユーザーの接続だけを止められます。
UNLOCK TABLES はほかの接続のロックを解除しない
UNLOCK TABLES が解放するのは、その文を実行した接続自身が LOCK TABLES(または FLUSH TABLES WITH READ LOCK)で取ったロックだけです。別の接続から実行しても、ほかの接続のロックは外れません。ほかの接続が LOCK TABLES で握ったロックを外すには、その接続を KILL します(接続が切れると、その接続のテーブルロックは自動で解放されます)。
KILL の前に確認すること
- 止める処理が何か: バックアップ、プラグインの更新処理、データの移行などを途中で止めると、その処理は最初からやり直しになります。
- MyISAM の REPAIR TABLE・OPTIMIZE TABLE は止めない: MySQL のマニュアルでは、MyISAM のテーブルに対するこれらの処理を
KILLすると、テーブルが壊れて使えなくなると説明されています。 - トランザクションを使わない処理は取り消されない: MyISAM のテーブルへの
UPDATEやDELETEを途中で止めると、そこまでの変更は残ります。
手順4: 解除できたことを確認する
SHOW FULL PROCESSLIST;
SELECT * FROM sys.innodb_lock_waits;
SELECT * FROM sys.schema_table_lock_waits WHERE waiting_pid <> blocking_pid;
待っていた処理が消えるか、実行中に変わっていれば解除できています。WordPress のページや管理画面が動くことも確認します。
再発を防ぐ
テーブルを InnoDB にそろえる
MyISAM はテーブル単位でロックするため、書き込みのたびにテーブル全体が待たされます。古いサイトでは MyISAM のテーブルが残っていることがあります。次のクエリで確認します(wp はデータベース名に置き換えます)。
SELECT TABLE_NAME, ENGINE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'wp' AND ENGINE <> 'InnoDB';
変換は ALTER TABLE テーブル名 ENGINE=InnoDB; で行います。変換中はそのテーブルへの書き込みが待たされるため、バックアップを取り、アクセスの少ない時間に行います。
処理の書き方と設定を見直す
- バックアップでテーブルをロックしない:
mysqldumpは、InnoDB のテーブルなら--single-transactionを付けると、テーブルをロックせずに一貫したバックアップを取れます。 - ALTER TABLE の待ち時間を短くする: メタデータロックの待ち時間(
lock_wait_timeout)の既定値は 1 年です。構造を変える前にセッションで短くしておくと、ほかの処理を長く止めずに済みます。
SET SESSION lock_wait_timeout = 10;
ALTER TABLE wp_postmeta ADD INDEX idx_example (meta_key(50));
- 行ロック待ちの上限を知っておく: InnoDB の行ロック待ちは
innodb_lock_wait_timeout(既定 50 秒)を過ぎるとエラーになります。 - トランザクションを開いたままにしない: 自作のスクリプトや管理ツールで
START TRANSACTIONしたら、すぐにCOMMITかROLLBACKします。
まとめ
SHOW FULL PROCESSLISTで、待たされている処理(Waiting for table metadata lockなど)と長く続く処理を見つけるsys.innodb_lock_waits(行ロック)とsys.schema_table_lock_waits(メタデータロック)で、ロックを握っている接続のIdを特定する- その接続を
KILLすると、コミットされていない変更が取り消され、ロックが解放される。UNLOCK TABLESはほかの接続のロックを解除しない - MyISAM の修復・最適化の処理は止めない。再発防止には InnoDB への統一、
--single-transactionでのバックアップ、lock_wait_timeoutの調整が有効