PHPのプログラムがメモリオーバーで落ちてしまうのに困っています。


全体で20000件ほど処理対象があるのですが、(だいたい)120件以上同時に処理をすると落ちてしまいます。
GETでスタートとエンドを指定できるので、下記のように100件ずつ、ブラウザでひとつひとつURLをたたけば動作します。
http://sample.com/sample.php?Start=0&end=99
http://sample.com/sample.php?Start=100&end=199
http://sample.com/sample.php?Start=200&end=299
....

ただし、定期的に実行したい処理なのでこれでは運用できません。

javascriptで次々パラメータを変えて呼び出すことも考えましたが、夜間にcron実行することも想定していますので、javascriptは使えません。

100件の処理を終えたのをハンドリングして、次の処理をする。そしてまたそれが終わったら次の処理をするという構成を作れれば解決するのかと思っています。

どういうやりかたをすればいいか、教えて頂けないでしょうか。


PHPバージョンは5.2
サクラレンタルサーバーのスタンダードプランです。

回答の条件
  • 1人2回まで
  • 登録:
  • 終了:2010/02/11 11:57:03
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

ベストアンサー

id:t-wata No.3

回答回数82ベストアンサー獲得回数13

ポイント50pt

http://www.gnu.org/software/wget/wget.html

wgetがあるなら、

http://sample.com/sample.php?Start=0&end=99

http://sample.com/sample.php?Start=100&end=199

http://sample.com/sample.php?Start=200&end=299

......

のようなlist.txtというファイルを作り、

wget -i list.txt

でOKです。

これならシェルスクリプトやバッチファイルでもできるのでcronやタスクスケジューラからの実行も可能です。

id:cottontonton

これはものすごく、いいかもしれません。

設計もこちらはらくにできそうなきがします。

ありがとうございます!!!

2010/02/07 23:29:39

その他の回答3件)

id:km1967 No.1

回答回数541ベストアンサー獲得回数40

ポイント50pt

どのようなプログラムなのか判らないので

確実な回答はできませんが、

スクリプトの最後で

header("Location: http://sample.com/sample.php?Start=$start&end=$end");

のようにすることでしょうか。

$start, $endには次に実行する番号を入れておきます

また、どの値になったら終了するか、終了条件を明示する必要があります


それから、このような方法で処理を分断したとしても

CPU負荷が高いと見なされ、レンタルサーバ業者からスクリプトの使用中止を指示される可能性が高いですね

id:cottontonton

ご回答ありがとうございます!

早速ためしてみたいと思います!

>CPU負荷が高いと見なされ、レンタルサーバ業者からスクリプトの使用中止を指示される可能性が高いですね

↑なるほど。。CPU的な負荷もですが、メモリのほうに影響が大きいプログラムですので、緊急避難的にご回答頂いた方法でしのげると思います。その間に、根本解決試みます。

素早い回答頂きまして、本当に有難うございます。

2010/02/07 00:38:26
id:e55ind No.2

回答回数162ベストアンサー獲得回数4

ポイント23pt

メモリーオーバーで落ちているというのは、どういう根拠で言ってるんでしょうか?

単に処理時間がかかるので、ブラウザのタイムアウトにあって、

表面上、処理が中断されたように見えるだけで

実際には実行されていると言うパターンのように見えます。

えぇ、そんなに簡単にメモリーオーバーにならないですけどね。

id:cottontonton

ご回答ありがとうございます!

処理の途中で、下記のエラーメッセージが表示されてしまいます。

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 1245184 bytes) in

後半の処理でテーブルインサートしているのですが、正常に終了するとき(100件程度のとき)は上記のメッセージはでませんし、インサートもできているのですが、件数が多いときにはテーブルに入っていません。


どこでメモリを喰っているか、的確に探し出すこともできず、、困ってしまっています。

変数のクリア漏れも一通り洗ったのですが、解決できません。

dBug.phpでの確認でも、おかしなものは見つからなかったのですが、もしメモリを喰っているものを探しだせる方法ご存知でしたら、それも教えて頂けるととてもありがたいです。

2010/02/07 01:17:51
id:t-wata No.3

回答回数82ベストアンサー獲得回数13ここでベストアンサー

ポイント50pt

http://www.gnu.org/software/wget/wget.html

wgetがあるなら、

http://sample.com/sample.php?Start=0&end=99

http://sample.com/sample.php?Start=100&end=199

http://sample.com/sample.php?Start=200&end=299

......

のようなlist.txtというファイルを作り、

wget -i list.txt

でOKです。

これならシェルスクリプトやバッチファイルでもできるのでcronやタスクスケジューラからの実行も可能です。

id:cottontonton

これはものすごく、いいかもしれません。

設計もこちらはらくにできそうなきがします。

ありがとうございます!!!

2010/02/07 23:29:39
id:kn1967 No.4

回答回数2915ベストアンサー獲得回数301

ポイント50pt

>どこでメモリを喰っているか


php4 の時は結構苦労したけど、php5になって、かなり改善されましたね。

それでもまだメモリ喰いな面があるようで、ちょっと(?)したコツが必要なようです。

(私も偶にですが、数万のサイトに1つのphpスクリプトで、

 数時間かけて情報収集する事があり、最初は結構苦労しました。

 回答1の(ような)方法で一時的に対処していた時期もありました。

「そんなに簡単にメモリーオーバーにならない」というような、幸せ満開

の状態でいつも過ごせれば理想的ですが、現実はそうでは無いですよね。)


php本体のソースコードを追った訳ではなく経験則になりますが、

以下、参考になれば・・・。


(1)メモリが実際に開放されるタイミングを考える

例えば、単純にループしている場合

while (条件) {
  変数初期化
  いろいろな処理
  変数開放
} ← 実際にメモリが開放されるのはこの時点

ループの回数分だけメモリ消費が増大するという事のようです。

ループ内からサブルーチンを読んだ場合でも同様で、ループを抜けるまで、

開放はしていても、それらの使いまわしはしてくれない模様です。


さらにネストしている場合

if (条件) {
    while (条件) {}
    while (条件) {}
} ← 実際にメモリが開放されるのはこの時点

こちらは多様する事もあるので、問題はさらに深刻です。


(2)対応・対策

・ループ回数を減らす。

  これが第一。

  一度に指定するURLの数を減らせという意味ではないです。

  phpスクリプト内部でのループを見直すという意味です。

・ループ内の処理を軽減する。

  処理手順や計算式を見直し1つでも変数/式/演算子を減らせないかを検討する。

  (足し算一箇所削るだけでも20000回の足し算を削れるのですものね。)

・変数を肥大させない

  file_get_contents でファイルを一括読み込みする事などは止め、

  1行毎に読み込んで、都度出力してしまう等。

  (1行ずつのほうが手数が多くて、遅いように思うかもしれませんが、

  転送されてくる待ち時間等もあり、実はそれほど大きな違いには、

  ならなかったりします。)

・固定文字列は冒頭でdefineしておく

  処理速度向上にも、これが意外と効く。

・変数の展開を避ける

  "これは$aである" よりも 'これは' . $a . 'である'

  コーディングの面倒さとの兼ね合いになるが処理速度のほうに貢献してくれる。

・ifなどで囲まない

  メモリ使用量を優先するか、処理時間を優先するかのせめぎ合いなので、

  この検討は後にしてます。

など・・・。


具体的に、どこを改善すべきかはスクリプトそのものを見てみないと判らないけど、

多分、全面的なスクリプトの見直しになると思われますので、

既に提示されているような方法で こまめに何度かに分けて、

スクリプトを呼び出すほうが、取り敢えずは良いかもしれません。


取り敢えず動くようにして、それから、チューニングを施す・・・。

それでもダメな場合は、はてなで、処理を軽減したい部分のチューニングを、

依頼してみても良い・・・かも・・・しれません。


余談ですが、

cronでの動作がメインであるならばWeb向けではなくCLI向けとして、

リプログラミングして呼び出すようにしたほうが楽かもしれません。

(apacheを通して使う場合とはスクリプトの方向性が違うので、

 製作に戸惑うかもしれず、是非にとまでは申せません。)


ちなみに、さくらでのCLI版の動かし方の例は下記になります。

http://support.sakura.ad.jp/page/news/20090303-004.news

cd /home/アカウント名/www/programs ; /usr/local/bin/php プログラム名

id:cottontonton

おおおお。

なるほどです。

foreach($hairetu as $target){

$this->omotaisyori(); //冒頭で関数初期化。

}

こんな感じで処理をまわしても、どんどんメモリが大きくなっていくので、なんでだろうと不思議におもっていたところでした。

メモリ開放のタイミング、、なるほどです。非常に参考になりました。

CLI化ですか。。すこし、難しそうでとっつきにくいですが、これまで全然選択肢になかったので、新しい視界がひらけた気がします。

ご丁寧な回答、ありがとうございます!

2010/02/07 23:36:59

コメントはまだありません

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません