これは この関連する質問 から生まれました。ここでは、2つのトランザクションを簡単なケース(両方が単一の行でのみ動作している)で連続的に発生させる方法を知りたかったのです。両方のトランザクションの最初の行としてSELECT ... FOR UPDATE
を使用して回答を得ましたが、これにより問題が発生します:最初のトランザクションがコミットまたはロールバックされない場合、2番目のトランザクションinnodb_lock_wait_timeout
変数は、2番目のトランザクションを実行しようとするクライアントに「申し訳ありませんが、もう一度試す」と通知されるまでの秒数を設定します...私の知る限り、次のサーバーが再起動するまで再試行します。そう:
ROLLBACK
を強制する方法があるはずです。デーモンを使用してこのようなトランザクションを強制終了する必要があります。その場合、そのようなデーモンはどのように見えますか?wait_timeout
またはinteractive_timeout
によって接続が強制終了された場合、トランザクションはロールバックされますか?これをコンソールからテストする方法はありますか?説明:innodb_lock_wait_timeout
は、トランザクションがロックが解放されるのを待つ秒数を設定します。私が欲しいのはロックを強制的に解放する方法です
更新1:次に、2番目のトランザクションが最初のトランザクションによってブロックされないようにするためにinnodb_lock_wait_timeout
が不十分な理由を示す簡単な例を示します。
START TRANSACTION;
SELECT SLEEP(55);
COMMIT;
デフォルト設定のinnodb_lock_wait_timeout = 50
では、このトランザクションは55秒後にエラーなしで完了します。また、UPDATE
行の前にSLEEP
を追加し、同じ行をSELECT ... FOR UPDATE
しようとする別のクライアントから2番目のトランザクションを開始すると、2番目のトランザクションがタイムアウトになります。眠ってしまった方。
私が探しているのは、このトランザクションの安らかな眠りを終わらせる方法です。
更新2:上記の例がどれほど現実的であるかについてのhobodaveの懸念に応えて、ここに代替シナリオがあります:DBAがライブサーバーに接続して実行します
START TRANSACTION
SELECT ... FOR UPDATE
2行目では、アプリケーションが頻繁に書き込む行をロックしています。次に、DBAは中断され、トランザクションの終了を忘れて立ち去ります。アプリケーションは、行がロック解除されるまでグラインドして停止します。この間違いの結果としてアプリケーションがスタックする時間を最小限にしたいと思います。
このスレッドの半分以上は、ServerFaultで質問する方法についてのようです。質問は理にかなっており、非常に単純です:停止したトランザクションを自動的にロールバックするにはどうすればよいですか?
接続全体を強制終了する場合の1つの解決策は、wait_timeout/interactive_timeoutを設定することです。 https://stackoverflow.com/questions/9936699/mysql-rollback-on-transaction-with-lost-disconnected-connection を参照してください。
ここでServerFaultに関する質問が行われるので、特にシステム管理者やDBAが専門知識を持っている知識の分野で、MySQLの問題に対するMySQLソリューションを探していると想定するのは当然です。セクションはあなたの質問を扱います:
最初のトランザクションがコミットまたはロールバックされない場合、2番目のトランザクションは無期限にブロックされます
いいえ、ありません。 innodb_lock_wait_timeout
を理解していないと思います。それはまさにあなたが必要とするものを行います。
マニュアルに記載されているようにエラーで戻ります:
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
innodb_lock_wait_timeout
秒間非常に確実にブロックされます。デフォルトでは、トランザクションはロールバックされません。再試行するかロールバックするかに関係なく、このエラーの処理方法を決定するのはアプリケーションコードの責任です。
自動ロールバックが必要な場合は、マニュアルでも説明されています。
現在のトランザクションはロールバックされません。 (トランザクション全体をロールバックするには、
--innodb_rollback_on_timeout
オプションを指定してサーバーを起動します。
最初に、無期限にブロックしているfirstトランザクションをタイムアウトする方法が必要であることを「意味する」とコメントで述べています。これは、元の質問からは明らかではなく、「最初のトランザクションがコミットまたはロールバックされない場合、2番目のトランザクションは無期限にブロックされます」と競合します。
それでも、私はその質問にも答えることができます。 MySQLプロトコルには「クエリタイムアウト」はありません。つまり、最初にブロックされたトランザクションをタイムアウトすることはできません。完了するまで待つか、セッションを終了する必要があります。セッションが終了すると、サーバーはトランザクションを自動的にロールバックします。
他の唯一の代替策は、非ブロッキングI/Oを利用するmysqlライブラリを使用または書き込むことで、アプリケーションがスレッド/フォークを強制終了できるようにしますN秒後にクエリを実行します。このようなライブラリの実装と使用法は、ServerFaultの範囲を超えています。これは StackOverflow に適切な質問です。
次に、コメントで次のように述べています。
実際には、MySQLの終了時にトランザクションに時間がかかるシナリオよりも、トランザクション中にクライアントアプリがハングする(たとえば、無限ループに陥る)シナリオの方が問題でした。
これは、元の質問ではまったく明らかではありませんでしたが、それでも明らかではありません。これは、コメントでこのかなり重要な一口を共有した後にのみ識別できます。
これが実際に解決しようとしている問題である場合は、間違ったフォーラムで質問したと思われます。 MySQLが提供できない、このコミュニティの範囲外であるプログラミングソリューションを必要とするアプリケーションレベルのプログラミング問題について説明しました。あなたの最新の回答は、「Rubyプログラムが無限ループしないようにするにはどうすればよいですか?」という質問に答えます。この質問は、このコミュニティではトピック外であり、 StackOverflow 。
同様の状況で、私のチームはpt-kill( https://www.percona.com/doc/percona-toolkit/2.1/pt-kill.html )を使用することを決定しました。 (特に)実行時間が長すぎるクエリを報告または強制終了できます。 X分ごとにcronで実行することが可能です。
問題は、予想外に長い(例:不定)実行時間を持つクエリが、読み取りロックでテーブルをフラッシュしようとしたバックアッププロシージャをロックし、その後、「テーブルがフラッシュするのを待つ」という他のすべてのクエリがロックされ、その後タイムアウトすることがなかったことです。ロックを待機していないため、アプリケーションでエラーが発生せず、最終的には管理者へのアラートがなく、アプリケーションが停止していました。
修正は明らかに最初のクエリを修正することでしたが、将来偶発的に発生する状況を回避するために、特定の時間よりも長く続くトランザクション/クエリを自動終了(またはレポート)することが可能であるとすばらしいでしょう。質問のどの作者が尋ねているようです。これはpt-killで実行できます。
簡単な解決策は、必要なtimeout
時間よりも時間がかかるクエリを強制終了するストアドプロシージャを用意し、innodb_rollback_on_timeout
オプションを使用することです。