MySQLでロック待機タイムアウト超過をデバッグする方法は?


269

私のプロダクションエラーログで時々見ます:

SQLSTATE [HY000]:一般エラー:1205ロック待機タイムアウトを超えました。トランザクションを再開してみてください

その時点でどのクエリがデータベースにアクセスしようとしているのかわかっていますが、その正確な瞬間にどのクエリがロックしていたかを調べる方法はありますか?


1
私は誰もがEirikの答えを
試す

回答:


261

これを与えるのは、トランザクションという言葉です。ステートメントによって、クエリが1つ以上のInnoDBテーブルの少なくとも1つの行を変更しようとしたことがわかります。

クエリを知っているので、アクセスされているすべてのテーブルが原因の候補です。

そこから、あなたは走ることができるはずです SHOW ENGINE INNODB STATUS\G

影響を受けるテーブルを表示できるはずです

あらゆる種類の追加のロックおよびミューテックス情報を取得します。

これは私のクライアントの1つからのサンプルです:

mysql> show engine innodb status\G
*************************** 1. row ***************************
  Type: InnoDB
  Name:
Status:
=====================================
110514 19:44:14 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 4 seconds
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 9014315, signal count 7805377
Mutex spin waits 0, rounds 11487096053, OS waits 7756855
RW-shared spins 722142, OS waits 211221; RW-excl spins 787046, OS waits 39353
------------------------
LATEST FOREIGN KEY ERROR
------------------------
110507 21:41:35 Transaction:
TRANSACTION 0 606162814, ACTIVE 0 sec, process no 29956, OS thread id 1223895360 updating or deleting, thread declared inside InnoDB 499
mysql tables in use 1, locked 1
14 lock struct(s), heap size 3024, 8 row lock(s), undo log entries 1
MySQL thread id 3686635, query id 124164167 10.64.89.145 viget updating
DELETE FROM file WHERE file_id in ('6dbafa39-7f00-0001-51f2-412a450be5cc' )
Foreign key constraint fails for table `backoffice`.`attachment`:
,
  CONSTRAINT `attachment_ibfk_2` FOREIGN KEY (`file_id`) REFERENCES `file` (`file_id`)
Trying to delete or update in parent table, in index `PRIMARY` tuple:
DATA TUPLE: 17 fields;
 0: len 36; hex 36646261666133392d376630302d303030312d353166322d343132613435306265356363; asc 6dbafa39-7f00-0001-51f2-412a450be5cc;; 1: len 6; hex 000024214f7e; asc   $!O~;; 2: len 7; hex 000000400217bc; asc    @   ;; 3: len 2; hex 03e9; asc   ;; 4: len 2; hex 03e8; asc   ;; 5: len 36; hex 65666635323863622d376630302d303030312d336632662d353239626433653361333032; asc eff528cb-7f00-0001-3f2f-529bd3e3a302;; 6: len 40; hex 36646234376337652d376630302d303030312d353166322d3431326132346664656366352e6d7033; asc 6db47c7e-7f00-0001-51f2-412a24fdecf5.mp3;; 7: len 21; hex 416e67656c73204e6f7720436f6e666572656e6365; asc Angels Now Conference;; 8: len 34; hex 416e67656c73204e6f7720436f6e666572656e6365204a756c7920392c2032303131; asc Angels Now Conference July 9, 2011;; 9: len 1; hex 80; asc  ;; 10: len 8; hex 8000124a5262bdf4; asc    JRb  ;; 11: len 8; hex 8000124a57669dc3; asc    JWf  ;; 12: SQL NULL; 13: len 5; hex 8000012200; asc    " ;; 14: len 1; hex 80; asc  ;; 15: len 2; hex 83e8; asc   ;; 16: len 4; hex 8000000a; asc     ;;

But in child table `backoffice`.`attachment`, in index `PRIMARY`, there is a record:
PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 30; hex 36646261666133392d376630302d303030312d353166322d343132613435; asc 6dbafa39-7f00-0001-51f2-412a45;...(truncated); 1: len 30; hex 38666164663561652d376630302d303030312d326436612d636164326361; asc 8fadf5ae-7f00-0001-2d6a-cad2ca;...(truncated); 2: len 6; hex 00002297b3ff; asc   "   ;; 3: len 7; hex 80000040070110; asc    @   ;; 4: len 2; hex 0000; asc   ;; 5: len 30; hex 416e67656c73204e6f7720436f6e666572656e636520446f63756d656e74; asc Angels Now Conference Document;;

------------
TRANSACTIONS
------------
Trx id counter 0 620783814
Purge done for trx's n:o < 0 620783800 undo n:o < 0 0
History list length 35
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 0 0, not started, process no 29956, OS thread id 1192212800
MySQL thread id 5341758, query id 189708501 127.0.0.1 lwdba
show innodb status
---TRANSACTION 0 620783788, not started, process no 29956, OS thread id 1196472640
MySQL thread id 5341773, query id 189708353 10.64.89.143 viget
---TRANSACTION 0 0, not started, process no 29956, OS thread id 1223895360
MySQL thread id 5341667, query id 189706152 10.64.89.145 viget
---TRANSACTION 0 0, not started, process no 29956, OS thread id 1227888960
MySQL thread id 5341556, query id 189699857 172.16.135.63 lwdba
---TRANSACTION 0 620781112, not started, process no 29956, OS thread id 1222297920
MySQL thread id 5341511, query id 189696265 10.64.89.143 viget
---TRANSACTION 0 620783736, not started, process no 29956, OS thread id 1229752640
MySQL thread id 5339005, query id 189707998 10.64.89.144 viget
---TRANSACTION 0 620783785, not started, process no 29956, OS thread id 1198602560
MySQL thread id 5337583, query id 189708349 10.64.89.145 viget
---TRANSACTION 0 620783469, not started, process no 29956, OS thread id 1224161600
MySQL thread id 5333500, query id 189708478 10.64.89.144 viget
---TRANSACTION 0 620781240, not started, process no 29956, OS thread id 1198336320
MySQL thread id 5324256, query id 189708493 10.64.89.145 viget
---TRANSACTION 0 617458223, not started, process no 29956, OS thread id 1195141440
MySQL thread id 736, query id 175038790 Has read all relay log; waiting for the slave I/O thread to update it
--------
FILE I/O
--------
I/O thread 0 state: waiting for i/o request (insert buffer thread)
I/O thread 1 state: waiting for i/o request (log thread)
I/O thread 2 state: waiting for i/o request (read thread)
I/O thread 3 state: waiting for i/o request (write thread)
Pending normal aio reads: 0, aio writes: 0,
 ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
Pending flushes (fsync) log: 0; buffer pool: 0
519878 OS file reads, 18962880 OS file writes, 13349046 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 6.25 writes/s, 4.50 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 1190, seg size 1192,
174800 inserts, 174800 merged recs, 54439 merges
Hash table size 35401603, node heap has 35160 buffer(s)
0.50 hash searches/s, 11.75 non-hash searches/s
---
LOG
---
Log sequence number 28 1235093534
Log flushed up to   28 1235093534
Last checkpoint at  28 1235091275
0 pending log writes, 0 pending chkp writes
12262564 log i/o's done, 3.25 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 18909316674; in additional pool allocated 1048576
Dictionary memory allocated 2019632
Buffer pool size   1048576
Free buffers       175763
Database pages     837653
Modified db pages  6
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages read 770138, created 108485, written 7795318
0.00 reads/s, 0.00 creates/s, 4.25 writes/s
Buffer pool hit rate 1000 / 1000
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
1 read views open inside InnoDB
Main thread process no. 29956, id 1185823040, state: sleeping
Number of rows inserted 6453767, updated 4602534, deleted 3638793, read 388349505551
0.25 inserts/s, 1.25 updates/s, 0.00 deletes/s, 2.75 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================

1 row in set, 1 warning (0.00 sec)

innodb_lock_wait_timeoutを設定して、InnoDBのロック待機タイムアウト値を増やすことを検討する必要があります。デフォルトは50秒です。

mysql> show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 50    |
+--------------------------+-------+
1 row in set (0.01 sec)

/etc/my.cnfこの行で永続的に高い値に設定できます

[mysqld]
innodb_lock_wait_timeout=120

mysqlを再起動します。この時点でmysqlを再起動できない場合は、次を実行します。

SET GLOBAL innodb_lock_wait_timeout = 120; 

セッション中に設定することもできます

SET innodb_lock_wait_timeout = 120; 

続いてクエリ


5
組み込みInnoDBの場合、innodb_lock_wait_timeout変数はサーバーの起動時にのみ設定できます。InnoDBプラグインの場合、起動時に設定するか、実行時に変更することができ、グローバル値とセッション値の両方があります。
Timo Huovinen 2013年

1
こんにちは@rolandomysqldba、あなたは私のこの記事に提案を私に与えてくださいすることができますstackoverflow.com/questions/18267565/...
マニッシュSapkal

2
最初のクエリを実行しようとしたとき、私はこのエラーを取得:SQL Error (1064): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\G' at line 1
イウリアンOnofrei

1
@Pacerier mysqldが再起動されるたびに、SET GLOBAL innodb_lock_wait_timeout = 120;再度実行する必要があります。/etc/my.cnfオプションがある場合は、innodb_lock_wait_timeoutが設定されています。誰もが他のみんな(に対してグローバルに変更するSUPER権限を持っているdev.mysql.com/doc/refman/5.6/en/...
RolandoMySQLDBA

3
@IulianOnofrei \ G文字はMySQLコマンドラインの特別な機能であり、出力の表示方法を変更します。他のMySQLクライアントでは、代わりに通常のセミコロンを使用してください。
thenickdude 2017

83

この問題に関する多くのSOスレッドの1つで誰かが述べたように:テーブルをロックしているプロセスがプロセスリストでスリープしているように見えることがあります!問題のデータベースで開いていたすべてのスリープ状態のスレッド(現時点ではアクティブでなかった)をすべて停止するまで、髪を引きちぎっていました。最後にテーブルのロックを解除し、更新クエリを実行しました。

コメンターは、「MySQLスレッドがテーブルをロックし、MySQLに関連しない何かが発生するのを待つ間、スリープする場合がある」と似ていると述べています。

show engine innodb statusログを再確認した後(ロックの原因となったクライアントを追跡した後)、スタックしているスレッドがトランザクションリストの一番下、エラーが発生しそうなアクティブなクエリの下に表示されていることに気付きました凍結されたロックのために:

------------------
---TRANSACTION 2744943820, ACTIVE 1154 sec(!!)
2 lock struct(s), heap size 376, 2 row lock(s), undo log entries 1
MySQL thread id 276558, OS thread handle 0x7f93762e7710, query id 59264109 [ip] [database] cleaning up
Trx read view will not see trx with id >= 2744943821, sees < 2744943821

(「Trx read view」メッセージが凍結されたロックに関連しているかどうかは不明ですが、他のアクティブなトランザクションとは異なり、これは発行されたクエリには表示されず、代わりにトランザクションが「クリーンアップ」であると主張しますが、複数の行ロック)

この話の教訓は、スレッドがスリープ状態であってもトランザクションがアクティブになる可能性があるということです。


2
あなたが私の命を救ったとは言えませんが、あなたは確かに平和を心に留めています。あなたの答えを読んで、私は3260秒間アクティブであり、どこにも表示されない不気味なスレッドを見つけました。それを殺した後、私のすべての問題は解決されました!
kommradHomer 2015

これが私の問題でした。Railsアプリの遅延ジョブを適切に実行できなかった、20,000秒の時間のスリープ状態のトランザクション。ありがとう@Eirik
bigtex777

眠っているトランザクションがとにかく殺されない理由は何ですか?同様に、トランザクションが終了する必要があるタイムアウトを設定できますか?
patrickdavey 2017

1
トランザクションのロックの検索に役立つ可能性のある他のコマンド:show processlist;現在実行中のプロセスの完全なリストを表示します。これは、の圧縮バージョンであるため便利ですshow engine innodb status\g。また、dbがAmazon RDSインスタンス上にある場合は、を使用CALL mysql.rds_kill(<thread_id>);してスレッドを強制終了できます。それは私がプレーンよりも多くのプロセスを殺すことを可能にしたので、私は思うより高い許可を持っていkill <thread_id>;ます
nickang

1
誰かがこれのソースを持っています-おそらく、ロックがCOMMITフェーズの前に配置されていることを説明するドキュメントページですか?この正確な問題を確認し、ロックを保持していたスリープ状態のスレッドを強制終了することで問題が解決されたにもかかわらず、何も見つかりませんでした。
Erin Schoonover、2018

42

MySQLの人気により、ロック待機タイムアウトが超過したのも不思議ではありませんトランザクションの例外を再起動してみてください SOは非常に注目されています。

競合が多いほど、デッドロックの可能性が高くなります。デッドロックは、DBエンジンがデッドロックされたトランザクションの1つをタイムアウトすることで解決します。また、多数のエントリ(UPDATEまたはDELETE)を変更した長時間のトランザクション(ハイパフォーマンスJava Persistenceブックで説明されているようにダーティライトの異常を回避するためにロックがかかる)は、他のトランザクションとの競合を発生させる可能性が高くなります。

InnoDB MVCCですがFOR UPDATE句を使用して明示的なロックをリクエストすることもできます。ただし、他の一般的なDB(Oracle、MSSQL、PostgreSQL、DB2)とは異なり、MySQL REPEATABLE_READデフォルトの分離レベルとして使用します

これで、取得したロック(行を変更するか、明示的なロックを使用して)が、現在実行中のトランザクションの間保持されます。ロックREPEATABLE_READとの違いについての適切な説明が必要な場合は、このPerconaの記事READ COMMITTEDをお読みください。

REPEATABLE READでは、トランザクション中に取得されたすべてのロックは、トランザクションの期間中保持されます。

READ COMMITTEDでは、STATEMENTの完了後に、スキャンと一致しなかったロックが解放されます。

...

つまり、READ COMMITTEDでは、他のトランザクションは、UPDATEステートメントが完了すると(REPEATABLE READで)更新できなかった行を自由に更新できます。

したがって、分離レベル(REPEATABLE_READSERIALIZABLE)の制限が高いほど、デッドロックの可能性が高くなります。これは「それ自体」の問題ではなく、トレードオフです。

複数のHTTPリクエストにまたがる論理トランザクションを使用する場合、アプリケーションレベルの失われた更新の防止READ_COMMITEDが必要になるため、で非常に良い結果を得ることができます。楽観的ロックアプローチの目標は、アップデート失ったあなたが使用している場合でも起こる可能性がある分離レベルを使用できるようにすることで、ロック競合を削減しながらします。SERIALIZABLEREAD_COMMITED


4
ロック待機タイムアウトはデッドロックとは異なりませんか?たとえば、1つのスレッドが正当な理由で60秒間ロックを保持している場合、ロック待機タイムアウトが発生する可能性があります。本当にデッドロックがある場合、MySQLがこれを検出してトランザクションを即座に強制終了し、これがロック待機タイムアウトに関連しないというのは本当ですか?
ColinM 2014年

1
あなたが正しいです。DBはタイムアウト後にデッドロックを検出し、待機中のプロセスを1つ強制終了するため、1つのトランザクションが勝ち、もう1つのトランザクションは失敗します。ただし、ロックを長く保持すると、アプリケーションのスケーラビリティが低下します。デッドロックに遭遇しない場合でも、アプリケーションのランタイム動作のシリアライズ可能な部分を増やします。
Vlad Mihalcea 2014年

19

記録として、デッドロックがありMySQLがそれを検出できない場合にもロック待機タイムアウト例外が発生するため、タイムアウトするだけです。もう1つの理由は、クエリの実行時間が非常に長く、解決/修復が簡単な場合があるため、ここではこのケースについては説明しません。

MySQLは通常、2つのトランザクション内で「適切に」構築されたデッドロックを処理できます。次に、MySQLは、所有するロックが少ない1つのトランザクションを強制終了/ロールバックし(影響が少ない行に影響を与えるため、重要性は低くなります)、もう1つのトランザクションを終了させます。

ここで、2つのプロセスAとB、および3つのトランザクションがあるとします。

Process A Transaction 1: Locks X
Process B Transaction 2: Locks Y
Process A Transaction 3: Needs Y => Waits for Y
Process B Transaction 2: Needs X => Waits for X
Process A Transaction 1: Waits for Transaction 3 to finish

(see the last two paragraph below to specify the terms in more detail)

=> deadlock 

MySQLはデッドロック(3つのトランザクションにまたがる)があることを認識できないため、これは非常に残念なセットアップです。したがって、MySQLは...何もしません。何をすべきか分からないので、ただ待つだけです。最初に取得したロックがタイムアウトを超えるまで待機し(プロセスAトランザクション1:ロックX)、これによりロックXのブロックが解除され、トランザクション2などがロック解除されます。

技術は、最初のロック(ロックX)の原因(クエリ)を見つけることです。show engine innodb statusトランザクション3がトランザクション2を待機していることは簡単に確認できますが()、トランザクション2が待機しているトランザクション(トランザクション1)は確認できません。MySQLは、トランザクション1に関連付けられたロックやクエリを出力しません。唯一のヒントは、(show engine innodb status印刷出力の)トランザクションリストの最下部で、トランザクション1が何も実行していないように見えることです(ただし、実際にはトランザクション3の待機中です)。仕上げ)。

ここでは、待機している特定のトランザクションに対してロック(Lock X)が許可されるSQLクエリを見つける方法について説明します。 Tracking MySQL query history in long running transactions

例では、プロセスとトランザクションが正確に何であるか疑問に思っている場合。プロセスはPHPプロセスです。トランザクションは、innodb-trx-tableで定義されているトランザクションです。私の場合、2つのPHPプロセスがあり、それぞれで手動でトランザクションを開始しました。興味深い部分は、プロセスで1つのトランザクションを開始したにもかかわらず、MySQLは実際には2つの別個のトランザクションを内部で使用したことです(理由はわかりませんが、MySQLの開発者が説明できるかもしれません)。

MySQLは独自のトランザクションを内部で管理しており、(私の場合)2つのトランザクションを使用してPHPプロセス(プロセスA)からのすべてのSQL要求を処理することにしました。トランザクション1がトランザクション3の終了を待機しているというステートメントは、内部のMySQLのものです。MySQLはトランザクション1とトランザクション3を実際に "知っていた"(プロセスAからの)1つの "トランザクション"要求の一部としてインスタンス化されました。トランザクション3(「トランザクション」のサブパート)がブロックされたため、「トランザクション」全体がブロックされました。「トランザクション」がトランザクション1(「トランザクション」のサブパートでもある)を完了できなかったため、同様に完了していないとマークされました。これは、「トランザクション1がトランザクション3の終了を待つ」という意味です。


14

この例外の大きな問題は、通常はテスト環境で再現できないことと、prodで発生したときにinnodbエンジンステータスを実行することがないということです。したがって、プロジェクトの1つで、この例外のキャッチブロックに以下のコードを挿入しました。これは、例外が発生したときにエンジンのステータスを把握するのに役立ちました。それは大いに役立ちました。

Statement st = con.createStatement();
ResultSet rs =  st.executeQuery("SHOW ENGINE INNODB STATUS");
while(rs.next()){
    log.info(rs.getString(1));
    log.info(rs.getString(2));
    log.info(rs.getString(3));
}

11

pt-deadlock-loggerユーティリティの manページを見てください:

brew install percona-toolkit
pt-deadlock-logger --ask-pass server_name

engine innodb status上記から情報を抽出し、daemon30秒ごとに実行するを作成するためにも使用できます。



ロック待機タイムアウトはデッドロックと同じではありません。具体的には、デッドロックが検出されないため、innodbはそれらに関する情報を表示しません。そのため、pt-deadlock-loggerは役に立ちません。
Jay Paroline

ロックタイムアウトとデッドロックは関連していますが、dev.mysql.com / doc / refman / 5.7
Andrei Sura

11

上記のRolandoの答えから推定すると、クエリをブロックしているのはこれらです。

---TRANSACTION 0 620783788, not started, process no 29956, OS thread id 1196472640
MySQL thread id 5341773, query id 189708353 10.64.89.143 viget

クエリを実行する必要があり、他のクエリが実行されるのを待つことができない場合は、MySQLスレッドIDを使用してそれらを強制終了します。

kill 5341773 <replace with your thread id>

(明らかにシェルではなく、mysql内から)

以下からスレッドIDを見つける必要があります。

show engine innodb status\G

コマンドを使用して、データベースをブロックしているコマンドを特定します。


1
どうやってそれがわかるの5341773?何が他と区別されるのかわかりません。
Wodin、2015年

いいえ、おそらくそのthreadIDではありません。これは例でした。"show engine innodb status \ G"コマンドからスレッドIDを見つけ、データベースをブロックしているスレッドIDを特定する必要があります。
Ellert van Koperen

1
ありがとう。つまり、言い換えると、たとえば1人ずつ殺すことなしに、それがどれであるかを知る方法はありません。
Wodin、2015年

トランザクションのリストで、実行中のトランザクションと実行時間を確認できます。したがって、それらを1つずつ削除する必要はありません。このリストは通常​​、何が起こっているのかをかなりよく示しています。
Ellert van Koperen

10

ロックタイムアウトの問題の原因となった「その他のクエリ」を特定するために私が最終的にしなければならないことは、次のとおりです。アプリケーションコードでは、このタスク専用の別のスレッドで保留中のすべてのデータベース呼び出しを追跡します。DB呼び出しにN秒より長い時間がかかる場合(ここでは30秒です)、ログに記録します。

-- Pending InnoDB transactions
SELECT * FROM information_schema.innodb_trx ORDER BY trx_started; 

-- Optionally, log what transaction holds what locks
SELECT * FROM information_schema.innodb_locks;

上記により、デッドロックの原因となった行をロックする同時クエリを正確に特定することができました。私の場合、それらはINSERT ... SELECT単純なSELECTとは異なり、基になる行をロックするようなステートメントでした。その後、コードを再編成するか、コミットされていない読み取りなどの別のトランザクション分離を使用できます。

幸運を!


9

以下を使用できます。

show full processlist

MySQLのすべての接続と現在の接続状態、および実行中のクエリが一覧表示されます。show processlist;切り捨てられたクエリと接続統計を表示する短いバリアントもあります。



-2

MySQLのgeneral.logをアクティブにし(ディスクに負荷がかかります)、mysql_analyse_general_log.plを使用して、長時間実行されているトランザクションを抽出します。例:

--min-duration = your innodb_lock_wait_timeout value

その後、general.logを無効にします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.