[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 メーリングリストの案内