[PHP-users 31397] Re: これは仕様?(sleep関数と非同期クエリ関数の関係)

Yoshihiro Hanahara hanahara @ meiko.co.jp
2007年 2月 6日 (火) 12:37:05 JST


花原@明宏です。


コードを実際には実行していませんが、コードを見ただけなのです。
もう解決しているかもしれませんね。


On Fri, 02 Feb 2007 16:07:43 +0900
神 谷 <djr_kamiya @ hotmail.com> wrote:

> psqlのコマンドラインで実行した場合には4.6秒程
> systemコマンドでpingを使ったsleep紛いのインターバルを使用した場合は18秒程
> 
> で結果が返ってくるのですが、sleep()、usleep()を使用してイン
> ターバルした際には388秒(6分28秒)もかかってしまう等の状況になって
> しまいます。

.....<<<途中省略>>>....


> 予想される原因は、非同期クエリpg_send_query()の動作に、sleep()、
> usleep()が影響して止まっているくらいしか、想像がつきません。
> 個人的な理想としては、sleep関数は非同期クエリに影響しないことを
> 望んでおり、CPUリソースを消費しないようにするには、そうあるべきでは
> ないかと思っています。
> 
> 原因が私の予想とは別のところにあるのかもしれませんが、もし私の予
> 想通りだとしたとき現在の動作が
> ・PHPの仕様として適切
> ・現在の動作仕様上避けられない
> ・機能実装上のミス
> のどれにあたるのか、ご存知ないでしょうか?

結論からいうと、「機能実装上のミス」のように思います(^_^;)。
説明の為に、ソースコードに行番号をつけました。

     1  <?php
     2  /* DB接続 */
     3  $ConStr  = "host="     . DB_HOST;
     4  $ConStr .= " port="     . DB_PORT;
     5  $ConStr .= " dbname="   . DB_NAME;
     6  $ConStr .= " user="     . DB_USER;
     7  $ConStr .= " password=" . DB_PASS;
     8  $DBCon   = pg_connect( $ConStr );
     9
    10  /* 現在時刻の取得 */
    11  $QueryExecutionStartTime = time();
    12
    13  /* SQLの実行 */
    14  $SQL = "select * from hoge_table";
    15  pg_send_query( $DBCon, $SQL );
    16  $QueryExecutionTime = time() - $QueryExecutionStartTime;
    17
    18  /* 実行中であればループ */
    19  while( pg_connection_busy( $l_DBCon ) === TRUE ) {
    20    $QueryExecutionTime = time() - $QueryExecutionStartTime; // 動作時間(秒を求める
    21    if ( $QueryExecutionTime > 2 ) { // 実行してから約1秒経ったらインターバルを挟む
    22  //    usleep( 1000000 ); // インターバル処理
    23  //    sleep( 1 );        // インターバル処理
    24      system("ping localhost -n 2 > null"); // インターバル処理
    25  }
    26  print $QueryExecutionTime;
    27  ?>

まず、CPUの気持ちになって考えてください。
16行目で現在時刻を取得し、時間のかかるクエリーなんで最初の19行目は真にな
りループに突入します。20〜21行目では2秒以上経過してないので(コメントには1
秒って書いてありますが...)、0〜2秒の間は全速力でこのループを回ります。
つまり、pg_connection_busy()を2秒経過するまでは全速力で呼び出しかけてま
す。

# PostgreSQL側としては、クエリ実行中に「もう終わった?ま〜だ〜???」
# というのをガンガン聞かれまくっていると言う事になります(^_^;)。

sleep関数が非同期クエリに影響を及ぼしているのではなく、影響が在るように
コーディングしていると言えます。
実行トレースのログを残すようにすれば分かりやすいですね。


このような場合、、

    define('MAX_QUERY_EXECUTION_TIME', 20); // 最大クエリー実行時間
    ...

    /* SQLの実行 */
    $SQL = "select * from hoge_table";
    pg_send_query( $DBCon, $SQL );

    /* 実行中であればループ */
    while( pg_connection_busy( $l_DBCon ) === TRUE ) {
      sleep( 1 );   // 1秒が長ければusleep()にする。でも、PHP 5.0.0 より
                    // 前のバージョンではusleep()は動作しない。

      $QueryExecutionTime = time() - $QueryExecutionStartTime; // 動作時間(秒を求める
      if ( $QueryExecutionTime >  MAX_QUERY_EXECUTION_TIME) {
        // 最大実行時間を超えたので中断する。
        // 中断フラグをセットするか、中断処理をここで行なう。
        break;
      }
    }

のようなコードにして、1秒に一回だけpg_connection_busy()を調べに行くよう
にすれば、劇的に実行速度が改善されると思います。
1秒より短いクエリーの場合は効率悪いですが、usleep使って検査周期を短くす
るか、PHPではおそらく簡単には出来ないと思うけど、マルチスレッドなコー
ドを書いてワーカースレッド内でpg_send_query()ではなく、pg_query()を使っ
て、通知をメインスレッドに上げるとかすれば、パフォーマンスはあがります
(別の問題を抱えてしまう可能性は大ですが... :-)。

---
Yoshihiro Hanahara <hanahara @ meiko.co.jp>





PHP-users メーリングリストの案内