MySQL / InnoDBでトランザクションの時間制限を設定する


11

これは、この関連する質問から生じました。ここでは、2つのトランザクションを単純なケース(両方が単一の行でのみ動作している)で連続して発生させる方法を知りたいと思いました。SELECT ... FOR UPDATE両方のトランザクションの最初の行として使用するという回答を得ましたが、これは問題につながります。最初のトランザクションがコミットまたはロールバックされない場合、2番目のトランザクションは無期限にブロックされます。innodb_lock_wait_timeout変数は、2番目のトランザクションを作成しようとしているクライアントは、「申し訳ありませんが、もう一度やり直してください」と言われるでしょう...しかし、私の知る限り、彼らは次のサーバを再起動するまで再試行されると思いまでの秒数を設定します。そう:

  1. ROLLBACKトランザクションが永遠にかかっている場合、強制する方法が必ずあるはずです。そのようなトランザクションを強制終了するためにデーモンを使用する必要があります。その場合、そのようなデーモンはどのようになりますか?
  2. 接続がトランザクションによってwait_timeoutまたはinteractive_timeoutトランザクションの途中で強制終了された場合、トランザクションはロールバックされますか?コンソールからこれをテストする方法はありますか?

明確化innodb_lock_wait_timeoutトランザクションがロックを解除するまで待機する秒数を設定します。私が望むのは、ロックを強制的に解除する方法です

更新1innodb_lock_wait_timeout2番目のトランザクションが最初のトランザクションによってブロックされないようにするのに十分ではない理由を示す簡単な例を次に示します。

START TRANSACTION;
SELECT SLEEP(55);
COMMIT;

のデフォルト設定ではinnodb_lock_wait_timeout = 50、このトランザクションは55秒後にエラーなしで完了します。またUPDATESLEEP行の前にを追加してからSELECT ... FOR UPDATE、同じ行を試行する別のクライアントから2番目のトランザクションを開始すると、スリープ状態になったトランザクションではなく、2番目のトランザクションがタイムアウトになります。

私が探しているのは、このトランザクションの安らかな眠りを終わらせる方法です。

更新2:上記の例がいかに現実的であるかについてのhobodaveの懸念に応えて、代替シナリオを次に示します。DBAはライブサーバーに接続して実行します

START TRANSACTION
SELECT ... FOR UPDATE

2行目は、アプリケーションが頻繁に書き込む行をロックします。その後、DBAは中断され、トランザクションの終了を忘れて立ち去ります。行がロック解除されるまで、アプリケーションは停止します。この間違いの結果としてアプリケーションがスタックする時間を最小限にしたいと思います。


最初の質問と完全に矛盾するように質問を更新しました。あなたは状態太字で「最初のトランザクションがバックを犯していないか、巻かないされている場合は、2番目のトランザクションが無期限にブロックされます。」これを最新の更新で反証します:「スリープ状態になったトランザクションではなく、タイムアウトするのは2番目のトランザクションです。」-まじで?!
hobodave

私のフレージングは​​もっと明確だったと思います。「無期限にブロック」とは、2番目のトランザクションが「トランザクションの再起動を試行してください」というエラーメッセージでタイムアウトすることを意味しました。しかし、それは再びタイムアウトになるので、そうすることは無益でしょう。したがって、2番目のトランザクションはブロックされます。タイムアウトを続けるため、完了しません。
トレバーバーナム

@Trevor:あなたのフレージングは​​完全に明確でした。質問を更新して、まったく別の質問をするだけです。これは非常に悪い形式です。
hobodave

問題は常に同じです。最初のトランザクションがコミットまたはロールバックされない場合、2番目のトランザクションが無期限にブロックされるという問題があります。完了するのにROLLBACKn秒以上かかる場合、最初のトランザクションでaを強制します。そうする方法はありますか?
トレバーバーナム

1
@TrevorBurnhamまたMYSQL、このシナリオを防ぐための構成がないのも不思議です。クライアントの無責任のためにサーバーがハングするのは許容できないためです。私はあなたの質問を理解するのに何の困難も見つけませんでした、それもとても関連しています。
Dinoopはpaloli

回答:


10

このスレッドの半分以上は、ServerFaultで質問する方法に関するもののようです。質問は理にかなっており、非常に簡単だと思います:停止したトランザクションをどのように自動的にロールバックしますか?

接続全体を強制終了する場合の解決策の1つは、wait_timeout / interactive_timeoutを設定することです。/programming/9936699/mysql-rollback-on-transaction-with-lost-disconnected-connectionを参照してください


3

ここではServerFaultで質問されているので、特にシステム管理者やDBAが専門知識を持っている知識の領域で、MySQLの問題に対するMySQLソリューションを探していると仮定するのは理にかなっています。セクションはあなたの質問に対処します:

最初のトランザクションがコミットまたはロールバックされない場合、2番目のトランザクションは無期限にブロックされます

いいえ、できません。あなたは理解していないと思いますinnodb_lock_wait_timeout。まさに必要なことを行います。

マニュアルに記載されているように、エラーとともに戻ります。

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
  • 定義により、これは不定ではありません。アプリケーションが再接続とブロックを繰り返している場合、アプリケーションはトランザクションではなく「無期限にブロック」しています。2番目のトランザクションは、innodb_lock_wait_timeout数秒間非常に確実にブロックします。

デフォルトでは、トランザクションはロールバックされません。このエラーをどのように処理するかを決定するのは、アプリケーションコードの責任です。再試行するかロールバックするかを決定します。

自動ロールバックが必要な場合は、マニュアルでも説明されています。

現在のトランザクションはロールバックされません。(トランザクション全体をロールバックするには、--innodb_rollback_on_timeoutオプションでサーバーを起動します。


RE:多数の更新とコメント

最初に、無期限にブロックしている最初のトランザクションをタイムアウトする方法が必要であることを「意味する」とコメントに述べています。これは元の質問からは明らかではなく、「最初のトランザクションがコミットまたはロールバックされない場合、2番目のトランザクションは無期限にブロックされます」と矛盾します。

それにもかかわらず、私もその質問に答えることができます。MySQLプロトコルには「クエリタイムアウト」はありません。これは、最初にブロックされたトランザクションをタイムアウトできないことを意味します。終了するまで待つか、セッションを終了する必要があります。セッションが終了すると、サーバーはトランザクションを自動的にロールバックします。

他の唯一の選択肢は、アプリケーションがN秒後にクエリを作成するスレッド/フォークを殺すことを許可する非ブロッキングI / Oを利用するmysqlライブラリを使用または記述することです。このようなライブラリの実装と使用法は、ServerFaultの範囲を超えています。これはStackOverflowにとって適切な質問です。

第二に、あなたはあなたのコメントで以下を述べました:

私は、MySQLの終了時にトランザクションが長時間かかるシナリオよりも、トランザクションの過程でクライアントアプリがハングする(たとえば、無限ループに陥る)シナリオに実際に疑問を持ちました。

これは元の質問ではまったく明らかではありませんでした。これは、コメントでこのかなり重要な情報を共有した後にのみ識別できます。

これが実際にあなたが解決しようとしている問題である場合、私はあなたが間違ったフォーラムでそれを尋ねたことを恐れています。MySQLが提供できないプログラミングソリューションを必要とし、このコミュニティの範囲外のアプリケーションレベルのプログラミングの問題について説明しました。最新の回答は、「Rubyプログラムが無限にループするのを防ぐにはどうすればよいですか?」という質問に答えます。この質問はこのコミュニティにとってトピック外であり、StackOverflowで質問する必要があります。


トランザクションは(名前との文書としてロックを待機しているときに真である間、申し訳ありませんが、innodb_lock_wait_timeoutそれは変更を行うために、クライアントがハングまたはクラッシュを取得する前に:お勧め)、それは私が提起した状況ではそうではありませんCOMMITROLLBACK
トレバーバーナム

@Trevor:あなたは単に間違っています。これは、あなたの側でテストするのは簡単です。私は最後にそれをテストしました。ダウン票を取り戻してください。
hobodave

@hobo、あなたの権利が彼がそれを好きにしなければならないというわけではないからです。
クリスS

クリス、彼の正体を説明してもらえますか?何の場合はどのように2番目のトランザクションが発生しませんCOMMITまたはROLLBACKメッセージが最初のトランザクションを解決するために送信されますか?Hobodaveさん、おそらくテストコードを提示していただければ助かります。
トレバーバーナム

2
@Trevor:あなたは少し理にかなっていない。トランザクションをシリアライズしたいですか?はい、あなたは他の質問でそう言って、この質問でそれを繰り返します。最初のトランザクションが完了していない場合、2番目のトランザクションが完了するとどうなりますか?その行でロックが解除されるのを待つように明示的に指示しました。したがって、待機する必要があります。これについて何を理解していないのですか?
hobodave

1

同様の状況で、私のチームはpt-kill(https://www.percona.com/doc/percona-toolkit/2.1/pt-kill.html)を使用することにしました。(特に)実行時間が長すぎるクエリについて報告または強制終了できます。その後、X分ごとにcronで実行できます。

問題は、予期しないほど長い(たとえば、無期限の)実行時間を持つクエリが、読み取りロックでテーブルをフラッシュしようとするバックアッププロシージャをロックし、「テーブルのフラッシュ待ち」で他のすべてのクエリをロックアウトして、タイムアウトしないことでしたロックを待機していないため、アプリケーションでエラーが発生せず、最終的に管理者へのアラートやアプリケーションの停止は発生しませんでした。

修正は明らかに最初のクエリを修正することでしたが、将来偶発的に発生する状況を回避するために、特定の時間よりも長く続くトランザクション/クエリを自動キル(またはレポート)することができれば素晴らしいでしょう。質問の著者は尋ねているようです。これはpt-killで実行できます。


0

簡単な解決策は、必要なtimeout時間よりも時間がかかるクエリを強制終了するストアドプロシージャを用意し、innodb_rollback_on_timeoutそれと共にオプションを使用することです。


-2

しばらくグーグルで調べた後、トランザクションごとの時間制限を設定する直接的な方法がないように見えます。これが私が見つけた最良の解決策です。

コマンド

SHOW PROCESSLIST;

現在実行中のすべてのコマンドと、それらが開始されてからの時間(秒単位)を示す表を提供します。クライアントがコマンドの提供を停止するSleepと、Time最後のコマンドが完了してからの秒数が列になり、コマンドの提供と説明されます。そのため、データベースでクエリが5秒を超えてはならないと確信している場合、手動で5秒以上KILLTime値を持つすべてのものにアクセスできます。たとえば、ID 3のプロセスのTime列の値が12の場合、次のようにできます。

KILL 3;

KILL構文のドキュメントは、そのIDを持つスレッドによって実行されているトランザクションがロールバックされることを示唆しています(これはテストしていませんが、注意してください)。

しかし、これを自動化し、5秒ごとにすべての残業スクリプトを強制終了する方法はありますか?同じページの最初のコメントからヒントが得られますが、コードはPHPにあります。我々が行うことができないようだSELECTSHOW PROCESSLISTのMySQLクライアント内から。それでも、毎秒実行するPHPデーモンがあるとすると、$MAX_TIME次のようになります。

$result = mysql_query("SHOW FULL PROCESSLIST");
while ($row=mysql_fetch_array($result)) {
  $process_id=$row["Id"];
    if ($row["Time"] > $MAX_TIME) {
      $sql="KILL $process_id";
      mysql_query($sql);
  }
}

これは、不正な動作だけでなく、アイドル状態のすべてのクライアント接続を強制的に再接続するという副作用があることに注意してください。

誰かがより良い答えを持っている場合、私はそれを聞きたいです。

編集:KILLこの方法で使用すると、少なくとも1つの重大なリスクがあります。上記のスクリプトをKILL、10秒間アイドル状態になっているすべての接続に使用するとします。接続が10秒間アイドル状態になった後、接続KILLが発行される前に、接続が強制終了されているユーザーがSTART TRANSACTIONコマンドを発行します。それからKILL編集されます。彼らはを送信しUPDATE、次に考え直し、を発行しROLLBACKます。ただし、KILL元のトランザクションがロールバックされたため、更新はすぐに行われ、ロールバックできません!テストケースについては、この投稿を参照してください。


2
このコメントは、この回答の今後の読者を対象としています。受け入れられた回答であっても、これを行わないでください。
-hobodave

OK、ダウン票を投じてスナイドコメントを残すのではなく、なぜこれが悪いアイデアになるのか説明してはどうでしょうか。元のPHPスクリプトを投稿した人は、サーバーがハングしないようにしていると言いました。同じ目標を達成するためのより良い方法があれば、見せてください。
トレバーバーナム

6
コメントはスナイドではありません。これは、将来のすべての読者が「ソリューション」を使用しようとしないことの警告です。長時間実行されているトランザクションを自動的に強制終了することは、一方的にひどい考えです。それは決して行われるべきではありません。それが説明です。セッションの強制終了は例外であり、通常ではありません。考えられる問題の根本的な原因は、ユーザーエラーです。行をロックして「立ち去る」DBA向けの技術的なソリューションは作成しません。あなたはそれらを解雇します。
hobodave

-2

hobodaveがコメントで示唆したように、一部のクライアント(mysqlコマンドラインユーティリティではないようですが)では、トランザクションの時間制限を設定することができます。次に、RubyでActiveRecordを使用したデモを示します。

require 'rubygems'
require 'timeout'
require 'active_record'

Timeout::timeout(5) {
  Foo.transaction do
    Foo.create(:name => 'Bar')
    sleep 10
  end
}

この例では、トランザクションは5秒後にタイムアウトし、自動的にロールバックされます。(hobodaveさんのコメントに応答して、更新:データベースが応答に5秒以上かかる場合は、トランザクションは、すぐにそれが早く-ないようにロールバックされます。)あなたが後にそのすべてのトランザクションがタイムアウト確保したい場合はn秒、 ActiveRecordのラッパーを作成できます。これは、Java、.NET、Pythonなどの最も一般的なライブラリにも当てはまると思いますが、まだテストしていません。(お持ちの場合は、この回答にコメントを投稿してください。)

ActiveRecordのトランザクションにKILLは、コマンドラインから実行されるトランザクションとは異なり、a が発行された場合に安全であるという利点もあります。/dba/1561/mysql-client-believes-theyre-in-a-transaction-gets-killed-wreaks-havocを参照してください

サーバー側で最大トランザクション時間を強制することは不可能と思われますが、他の回答で投稿したようなスクリプトを使用する場合を除きます。


ActiveRecordが同期(ブロッキング)I / Oを使用することを見落としています。スクリプトが実行しているのは、Rubyのスリープコマンドを中断することだけです。Foo.createコマンドが5分間ブロックされた場合、タイムアウトはコマンドを強制終了しません。これは、質問で提供されている例と互換性がありません。A SELECT ... FOR UPDATEは、他のクエリと同様に、長期間簡単にブロックできます。
-hobodave

ほぼすべてのmysqlクライアントライブラリがブロッキングI / Oを使用しているため、非ブロッキングI / Oを使用する独自のライブラリを使用または作成する必要があるという私の回答の提案に注意してください。タイムアウトが役に立たないことの簡単なデモを次に示します。gist.github.com
hobodave

私は、MySQLの終了時にトランザクションが長時間かかるシナリオよりも、トランザクションの過程でクライアントアプリがハングする(たとえば、無限ループに陥る)シナリオに実際に疑問を持ちました。しかし、ありがとう、それは良い点です。私はそれを指摘するために質問を更新しました。)
トレバーバーナム

申し立てられた結果を複製できません。使用するために要点を更新しましたActiveRecord::Base#find_by_sqlgist.github.com/856100繰り返しますが、これはARがブロッキングI / Oを使用しているためです。
-hobodave

@hobodaveはい、その編集は間違っていて修正されました。明確にするために:timeoutメソッドはトランザクションをロールバックしますが、MySQLデータベースがクエリに応答するまではロールバックしません。そのため、このtimeout方法は、クライアント側での長いトランザクションや、いくつかの比較的短いクエリで構成される長いトランザクションを防ぐのに適していますが、単一のクエリが原因である長いトランザクションには適していません。
トレバーバーナム
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.