私は次のMySQLのUPDATE
ステートメントを実行しています。
mysql> update customer set account_import_id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
トランザクションを使用していないのですが、なぜこのエラーが発生するのでしょうか。私は自分のMySQLサーバを再起動しようとさえしました、そして、それは助けになりませんでした。
テーブルには406,733行あります。
あなたはトランザクションを使用しています。 autocommitはトランザクションを無効にするのではなく、単にステートメントの最後にそれらを自動的にコミットさせるだけです。
何が起きているかというと、他のスレッドがあるレコードに対してレコードロックを長時間保持していて(テーブルのすべてのレコードを更新しているのです)、スレッドはタイムアウトしています。
あなたが発行することによってイベントの詳細を見ることができます
SHOW ENGINE INNODB STATUS
イベントの後(sql
エディタ内)。理想的には静かなテストマシンでこれを行います。
MySQLでロックされたテーブルのロックを解除する方法:
このようにロックを解除すると、データベース内で アトミック性 がロックを引き起こしたsqlステートメントに適用されないことがあります。
これは厄介です、そして適切な解決策はロックを引き起こしたあなたのアプリケーションを修正することです。しかし、ドルが並んでいるとき、Swiftのキックは物事を再び動かすでしょう。
1)MySQLに入る
mysql -u your_user -p
2)ロックされたテーブルのリストを見ましょう
mysql> show open tables where in_use>0;
3)現在のプロセスのリストを見てみましょう。それらの1つがあなたのテーブルをロックしています
mysql> show processlist;
4)これらのプロセスの1つを終了する
mysql> kill <put_process_id_here>;
mysql> set innodb_lock_wait_timeout=100
Query OK, 0 rows affected (0.02 sec)
mysql> show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 100 |
+--------------------------+-------+
もう一度ロックをかけます。データベースにSHOW ENGINE INNODB STATUS\G
を発行して、他のどのトランザクションが自分のトランザクションをロックしているのかを確認するのに100秒の時間があります。
データベースが微調整されているかどうかを確認してください。特にトランザクションの分離innodb_lock_wait_timeout変数を増やすことはお勧めできません。
Mysqlのcliでデータベースのトランザクション分離レベルを確認します。
mysql> SELECT @@GLOBAL.tx_isolation, @@tx_isolation, @@session.tx_isolation;
+-----------------------+-----------------+------------------------+
| @@GLOBAL.tx_isolation | @@tx_isolation | @@session.tx_isolation |
+-----------------------+-----------------+------------------------+
| REPEATABLE-READ | REPEATABLE-READ | REPEATABLE-READ |
+-----------------------+-----------------+------------------------+
1 row in set (0.00 sec)
分離レベルを変更することで改善を得ることができます。代わりにOracleのREAD COMMITTEDを使用してください。
mysql> SET tx_isolation = 'READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)
mysql> SET GLOBAL tx_isolation = 'READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)
mysql>
必要に応じてSELECT FOR UPDATEも使用してください。
提案された解決策のどれも私のために働きませんでしたが、これは働きました。
何かがクエリの実行をブロックしています。ほとんどの場合、別のクエリがクエリ内のいずれかのテーブルを更新、挿入、または削除しています。あなたはそれが何であるかを知る必要があります。
SHOW PROCESSLIST;
ブロックしているプロセスを見つけたら、そのid
を見つけて実行します。
KILL {id};
最初のクエリを再実行してください。
MarkRが言ったことと100%。自動コミットは、各文を1文のトランザクションにします。
SHOW ENGINE INNODB STATUS
は、デッドロックの理由についていくつかの手がかりを与えるはずです。遅いクエリログをよく調べて、他に何がテーブルをクエリしているのかを調べ、フルテーブルスキャンを実行しているものをすべて削除します。行レベルのロックはうまく機能しますが、すべての行をロックしようとしているときにはうまくいきません。
このテーブル内の他のレコードを更新できますか、それともこのテーブルは頻繁に使用されていますか。私が考えているのは、このレコードを更新する必要があるロックを獲得しようとしている間に、設定されたタイムアウトがタイムアウトになったということです。あなたは助けるかもしれない時間を増やすことができるかもしれません。
行数はそれほど大きくありません... account_import_idが主キーでない場合は、account_import_idにインデックスを作成します。
CREATE INDEX idx_customer_account_import_id ON customer (account_import_id);
データベーステーブルがInnoDBストレージエンジンとREAD-COMMITTEDトランザクション分離レベルを使用していることを確認してください。
SELECT @@ GLOBAL.tx_isolation、@@ tx_isolationで確認できます。 MySQLコンソールで。
READ-COMMITTEDに設定されていない場合は、設定する必要があります。設定する前に、mysqlでSUPER権限を持っていることを確認してください。
あなたは http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html から助けを借りることができます。
これを設定することで、あなたの問題は解決されると思います。
同時に2つのプロセスでこれを更新しようとしていないかどうかも確認することをお勧めします。ユーザー(@tala)がこの状況で同様のエラーメッセージを見つけました。
このようなことは私がphp言語の構成要素出口を使用していたときに私に起こりました。取引中です。それからこのトランザクションは「ハング」します、そしてあなたはmysqlプロセスを殺す必要があります(processlistで上で説明された)
私の例では、データを修正するために異常なクエリを実行していました。 クエリでテーブルをロックしている場合は、ロックタイムアウトに対処する必要はありません。
LOCK TABLES `customer` WRITE;
update customer set account_import_id = 1;
UNLOCK TABLES;
これはおそらく通常の使用にはお勧めできません。
より詳しい情報は: MySQL 8.0リファレンスマニュアル
パーティーに遅刻して(いつものように)しかし私の問題は私がいくつかの悪いSQLを書いたこと(初心者である)といくつかのプロセスがレコードをロックしていたという事実でした。私はSHOW PROCESSLIST
をしてからKILL <id>
を使ってIDを削除するだけで済みました。
私はこれに遭遇しました2つのDoctrine DBAL接続、そのうちの1つは(重要なログのために)非トランザクションとして、それらは互いに依存しないで並行して実行することを意図しています。
CodeExecution(
TransactionConnectionQuery()
TransactionlessConnectionQuery()
)
私の統合テストは非常にテストした後にデータロールバックのためのトランザクションにまとめられました。
beginTransaction()
CodeExecution(
TransactionConnectionQuery()
TransactionlessConnectionQuery() // CONFLICT
)
rollBack()
私の解決策は、これらのテストでラッピングトランザクションを無効にし、別の方法でdbデータをリセットすることでした。
大きなクエリを殺したばかりの場合は、rollback
に時間がかかります。強制終了されたクエリがロールバックされる前に別のクエリを発行すると、ロックタイムアウトエラーが発生する可能性があります。それが私に起こったことです。解決策は少し待つことでした。
詳細:
私はDELETEクエリを発行して、約100万行から約900,000行を削除しました。
これを誤って実行しました(行の10%のみを削除します)。DELETE FROM table WHERE MOD(id,10) = 0
これの代わりに(行の90%を削除します):DELETE FROM table WHERE MOD(id,10) != 0
10%ではなく、90%の行を削除したいと思いました。そのため、これまでに削除したすべての行をロールバックすることになるので、MySQLコマンドラインでプロセスを強制終了しました。
それから私はすぐに正しいコマンドを実行し、そしてすぐにlock timeout exceeded
エラーを受け取りました。ロックは実際にはバックグラウンドで行われているkillされたクエリのrollback
である可能性があることに気付きました。そこで数秒待ってからクエリを再実行しました。
私はグーグルから来ました、そして、私はちょうど私のために働いた解決策を加えたかったです。私の問題は、カスケード状態でFKが多い巨大なテーブルのレコードを削除しようとしていたため、OPと同じエラーが発生したことです。
私はautocommit
を無効にしてから、SQL文の最後にCOMMIT
を追加するだけでうまくいきました。私が理解している限りでは、これはコマンドの終わりで待つ代わりに少しずつバッファを解放します。
OPの例を続けるには、これでうまくいくはずです。
mysql> set autocommit=0;
mysql> update customer set account_import_id = 1; commit;
以前のようにMySQLの設定を終了したい場合は、autocommit
を再度アクティブにすることを忘れないでください。
mysql> set autocommit=1;