web-dev-qa-db-ja.com

mysqlクエリの最大実行時間を設定する方法は?

PHPのset_time_limit()のようなSQLクエリの最大実行時間を設定したいと思います。どのようにできるのか ?

23
microry

私はそれがもう少し長いと思っていましたが、 this によると、

MySQL 5.7.4では、最上位の読み取り専用SELECTステートメントに対して、ミリ秒単位で指定されたサーバー側の実行時間制限を設定する機能が導入されました。

SELECT 
/*+ MAX_EXECUTION_TIME = 1000 */ --in milliseconds
* 
FROM table;

これは、読み取り専用のSELECTステートメントでのみ機能することに注意してください。

更新:この変数はMySQL 5.7.4で追加され、MySQL 5.7.8ではmax_execution_timeに名前が変更されました。 ( ソース

22
Westy92

mysql native driver (php 5.3以降共通)、および mysqli 拡張を使用している場合、非同期クエリでこれを実現できます。

<?php

// Here's an example query that will take a long time to execute.
$sql = "
    select *
    from information_schema.tables t1
    join information_schema.tables t2
    join information_schema.tables t3
    join information_schema.tables t4
    join information_schema.tables t5
    join information_schema.tables t6
    join information_schema.tables t7
    join information_schema.tables t8
";

$mysqli = mysqli_connect('localhost', 'root', '');
$mysqli->query($sql, MYSQLI_ASYNC | MYSQLI_USE_RESULT);
$links = $errors = $reject = [];
$links[] = $mysqli;

// wait up to 1.5 seconds
$seconds = 1;
$microseconds = 500000;

$timeStart = microtime(true);

if (mysqli_poll($links, $errors, $reject, $seconds, $microseconds) > 0) {
    echo "query finished executing. now we start fetching the data rows over the network...\n";
    $result = $mysqli->reap_async_query();
    if ($result) {
        while ($row = $result->fetch_row()) {
            // print_r($row);
            if (microtime(true) - $timeStart > 1.5) {
                // we exceeded our time limit in the middle of fetching our result set.
                echo "timed out while fetching results\n";
                var_dump($mysqli->close());
                break;
            }
        }
    }
} else {
    echo "timed out while waiting for query to execute\n";
    var_dump($mysqli->close());
}

mysqli_query に与えているフラグは重要なことを達成します。クライアントドライバーに非同期モードを有効にするように指示し、より冗長なコードを使用するように強制しますが、タイムアウトを使用できます(必要に応じて同時クエリも発行できます!)。もう1つのフラグは、結果セット全体をメモリにバッファリングしないようにクライアントに指示します。

デフォルトでは、phpコードが結果の行へのアクセスを開始する前に、phpはmysqlクライアントライブラリを設定して、クエリの結果セット全体をメモリにフェッチします。これは、大きな結果を転送するのに時間がかかる場合があります。それを無効にします。そうしないと、バッファリングが完了するのを待つ間にタイムアウトになる危険性があります。

時間制限を超えているかどうかを確認する必要がある2つの場所があることに注意してください。

  • 実際のクエリ実行
  • 結果(データ)を取得中

PDOおよび通常のmysql拡張機能でも同様のことができます。非同期クエリはサポートされていないため、クエリの実行時間にタイムアウトを設定することはできません。ただし、バッファなしの結果セットはサポートされているため、少なくともデータのフェッチでタイムアウトを実装できます。

多くのクエリでは、mysqlは結果のストリーミングをほぼすぐに開始できるため、バッファーなしのクエリだけで、特定のクエリでタイムアウトをある程度効果的に実装できます。たとえば、

select * from tbl_with_1billion_rows

行のストリーミングをすぐに開始できますが、

select sum(foo) from tbl_with_1billion_rows

最初の行を返し始める前に、テーブル全体を処理する必要があります。後者の場合は、非同期クエリのタイムアウトにより節約できます。また、単純な古いデッドロックやその他のものからあなたを救います。

ps-接続自体にタイムアウトロジックを含めませんでした。

8
goat

この他のS.Oで答えを見つけることができます。質問:

MySQL-クエリの実行に許可される最大時間を制限できますか?

データベースサーバーで毎秒実行され、次のような接続と実行を行うcronジョブ:

  • プロセスリストを表示
  • クエリ時間が最大希望時間より長いすべての接続を検索します
  • これらの各プロセスに対してKILL [プロセスID]を実行します
5
Rafa

クエリを次のように書き直してください

select /*+ MAX_EXECUTION_TIME(1000) */ * from table

4
Manu Mohan T

pt_kill にはそのようなオプションがあります。しかし、それはオンデマンドであり、継続的な監視ではありません。 @Rafaが提案したことを行います。ただし、--sentinelcronに近づく方法のヒント。

0
Rick James