web-dev-qa-db-ja.com

メモリと時間のかかるphpタスク

この質問が以前に尋ねられた場合は申し訳ありませんが、使用できるものが見つかりませんでした。

私はクライアントのプロジェクトに取り組んでおり、現在、約3000レコードでまだ成長しているusersテーブルをループ処理する必要があります。

私は毎晩、cron/phpを使用して計算を行う必要があります。計算スクリプトは約3.5MBのメモリを使用し、実行に約1秒かかります。

個々のユーザーをロードするとき、私の現在のphpセットアップはこれをうまく処理しますが、ユーザーリストをループしようとすると、phpスクリプトの実行時間がなくなります。

私はいくつかの検索を行った後、ユーザーが計算するたびにページを自動的に再読み込みし、ループの前の場所を保持できることを読みました。これは良い考えのように聞こえますが、同様に処理した他の人からいくつかの意見を聞きたかったです状況と、これらのタイプのタスクの処理方法。

ありがとう。

3
Goddard

テーブルが大きくなることが本当に予想される場合は、プロセスのバッチ処理について考え始め、段階的に計算を行う必要があります。最も簡単な方法は、ユーザーIDとユーザーが最後に処理されたときのタイムスタンプを保持するセカンダリテーブルを用意し、cronスクリプトがループするように制限することです(たとえば、1時間あたり500ユーザー)。正確な数は、あなたが正確に何をしているのかに依存します、それは少しの試行錯誤です。

プロセスをバッチ処理することにした場合は、cronスクリプトを複数回実行する必要があることは明らかです。これは非常に簡単で、最近処理されていないユーザーのみを処理し(タイムスタンプを確認して)、もちろん、その後処理されます。ユーザーIDが連続している場合は、処理された各ユーザーIDをログに記録する手間を省いて、バッチの最後の1つだけをログに記録できますが、バッチの途中で問題が発生した場合、どこにあるかわからなくなります。止まった。あなたの選択 ;)

次に、ループから地獄を最適化する必要があります。簡単なものから始めますか、forまたはforeachを使用していますか?どちらか一方がもう一方よりも速いと主張するリファレンスはたくさんありますが、実際には、それらをテストして、どちらが速いか(実際に違いがある場合)を見つける必要があるでしょう。 PHPのバージョン、OS、およびループしている構造(たとえば、反復可能なオブジェクトをループしている場合)に依存します。特に、環境が異なる場合は、スクリプトが存在するサーバーでテストを実行する必要があります。あなたのローカル開発のもの。

次に、計算をプロファイリングして最適化します。あなたは何をしているのか教えてくれませんが、3.5MBのメモリは1回の反復で少し聞こえます。あなたの計算は非常に集中的であなたが最善を尽くしたかもしれません、あるいはあなたが見逃している明らかな何かがあるかもしれません、どんな場合でもそれはプロファイラーだけがあなたに言うことができるものです。

CLI SAPIの_max_execution_time_は0(制限なし)にハードコードされていますが、 set_time_limit または ini_set( 'max_execution_time') によって実行時間を制限したい場合があります。 (同じこと)2つの理由:

  1. (php.iniで)制限があるブラウザを介してスクリプトをテストするのに役立ちます。ブラウザーから本番スクリプトへのアクセスを許可することはお勧めできませんが、開発中は、スクリプトをテストするためだけにcronをセットアップしても意味がありません。
  2. CLIスクリプトに制限はありませんが、何か問題が発生した場合でも制限を課しても害はありません。データベースサーバーはときどき一時停止し、スクリプトで無限に(==メモリ不足になるまで)スクリプトを実行したくない場合があります。

メモリに問題がある場合は、いくつかの ガベージコレクション を実行するときがきました。素朴なアプローチは、スクリプトの最後で gc_collect_cycles を呼び出し、その時点での既存のサイクルのガベージコレクションを強制することです。事前に nset() メモリを消費するリソースがあれば、問題はありません。たとえば、phpループは独自のスコープを作成しないことに注意してください。

_<?php

foreach($array as $key => $value) {
   doSomething($value);
}

var_dump($key, $value);

?>
_

ループの最後の_$key_と_$value_をダンプして機能します。つまり、ループの終わりに1つの(_$array_)がなく、3つの未使用の変数があるため、 PHPがゴミを収集する良い時期だと判断したときに収集されます。強制するには、次のようにします。

_<?php

foreach($array as $key => $value) {
   doSomething($value);
}

unset($array, $key, $value);
gc_collect_cycles();

?>
_

ここではunset($array, $key, $value);は不要であると99%確信していますが、これは<php 5.3日間のお気に入りのハックであり、私はそれに固執しています(少なくとも、phpでのガベージコレクションの仕組みを完全に理解するまで)。 ;)。

それ以上のものについては、実際に計算の詳細を提供し、コードを提示する必要があります。

2
yannis

素朴な解決策は、PHPのリクエストタイムアウト設定を増やすことです。そのためのphp.ini設定があります。

しかし、あなたはcronジョブで実行しているので、なぜページをロードするのでしょうか-ジョブをphp-cliとして実行する必要があります。つまり、コマンドラインから、Web機能をまったく使用せずに(したがって、Apache以外でも)。 CLIスクリプトの場合、タイムアウトはありません(または、少なくとも、デフォルトのセットアップには何もありません。強制することが可能かどうかは不明です)。ほとんどのシステムでは、CLIバージョンは独自のphp.iniを使用します。つまり、必要に応じて、cronジョブにより多くのメモリ制限を与えることができます。

Php-cgi/mod_php/...を使用する場合は、スクリプトをcurlまたはwget呼び出しでラップして、cronジョブに配置できるようにする必要があります-これは不器用なソリューションであり、安全ではありません。これは、アプリケーションにリクエストを送信できる誰もがcronジョブをトリガーできるため、DoS攻撃の餌食になります。代わりにphp-cliを使用する場合、cronから直接スクリプトを実行できます:PHPはハッシュバング行(#!/usr/bin/env php)ですが、ほとんどの場合、phpを明示的に呼び出して、php.iniなどをオーバーライドできます。

2
tdammers