17

私のPHPアプリには、レコードをインポートできるインポートスクリプトがあります。

現在、CSVファイルからインポートしています。CSVファイルの各行をfgetcsvを使用して一度に1行ずつ読み取り、各行について、データベースクエリを含むそのレコードに対して多くの処理を実行してから、次の行に移動します。より多くのメモリを蓄積し続ける必要はありません。

約2500レコードがインポートされた後、PHPはメモリ制限(132 MB程度)を超えたと言って停止します。

CSVファイル自体はほんの数メガです-発生する他の処理は多くの文字列比較、差分などを行います。私はそれを操作する大量のコードを持っており、最小の複製を思い付くのは難しいでしょうサンプル'。

そのような問題を見つけて修正するための良い方法は何ですか?

問題の原因が見つかりました

実行時にすべてのデータベースクエリをログに記録するデバッグクラスがあります。そのため、約30KBの長さのSQLの文字列がメモリに残っていました。これは、長時間実行するように設計されたスクリプトには適していないことに気付きました。

他にもメモリリークの原因がある可能性がありますが、これが私の問題の原因であると確信しています。

4

8 に答える 8

7

実際に、クラッシュの原因となっているメモリ リークがスクリプトに 1 つまたは 2 つあると疑われる場合は、次の手順を実行する必要があります。

  • memory_limit500KB などの小さなサイズに変更する
  • 各行に適用される処理ステップを 1 つを除いてすべてコメントアウトします。
  • CSV ファイル全体に対して制限付きの処理を実行し、完了するかどうかを確認します。
  • ステップを徐々に追加して、メモリ使用量が急増するかどうかを確認します。

例:

ini_set('memory_limit', 1024 * 500);
$fp = fopen("test.csv", 'r');
while($row = fgetcsv($fp)) {
    validate_row($row);         // step 1: validate
    // add these back in one by one and keep an eye on memory usage
    //calculate_fizz($row);     // step 2: fizz
    //calculate_buzz($row);     // step 3: buzz
    //triangulate($row);        // step 4: triangulate
}
echo "Memory used: ", memory_get_peak_usage(), "\n";

最悪のシナリオは、すべての処理ステップが適度に非効率的であり、それらすべてを最適化する必要がある場合です。

于 2009-06-18T03:12:20.823 に答える
5

コードを確認すると役立ちますが、自分でデバッグする場合は、Xdebugを確認すると、アプリケーションのプロファイルを作成するのに役立ちます。

もちろん、何をしているのかによっては、メモリがいくらか蓄積されている可能性がありますが、2500レコードの場合は132MBがすでに高いようです。もちろん、必要に応じてphp.iniでメモリ制限を微調整できます。

読んでいるCSVファイルの大きさはどれくらいですか?そして、どのようなオブジェクトと種類の処理を行っていますか?

于 2009-06-18T02:02:33.210 に答える
2

変数を使用した後、変数をどのようにクリアするかによって異なります。

レコードは完成したように見えますが、まだどこかに情報を保存しています。疑わしい場合は、unset()を使用して変数をクリアします。

最小限の再現コードサンプルを提供して、これが役に立たない場合にすべてのメモリがどこに行くのかを確認してください。

ところで、問題を再現する最小のコードサンプルを作成することは、注意してコードを再度実行する必要があるため、優れたデバッグ手法です。

于 2009-06-18T02:01:38.547 に答える
2

php5.3 のローカル インストールを試して、http://www.php.net/manual/en/function.gc-collect-cycles.php を呼び出すことができます。

gc_collect_cycles— 既存のガベージ サイクルを強制的に収集します

状況が改善された場合、少なくとも問題を確認したことになります。

于 2009-06-18T06:44:10.250 に答える
1

ファイルをどのように読んでいますか?fread / filegetcontentsまたは他のそのような関数を使用している場合は、呼び出し時にファイル全体がロードされるため、メモリ内のファイルサイズ全体(またはfreadでロードする量)を消費します。ただし、fgetcsvを使用すると、行の長さに応じて一度に1行しか読み取れない場合、これはメモリ上で劇的に簡単になります。

また、各ループでできるだけ多くの変数を再利用していることを確認してください。大量のデータを含むアレイがないことを確認してください。

最後の注意として、ループの前にファイルを開いてから、後でファイルを閉じることも確認してください。

$fh = fopen(...);
while(true)
{
//...
}
fclose($fh);

あなたは本当にこれをしたくありません:

while(true)
{
$fh = fopen(...);
//...
fclose($fh);
}

そして、他の人が言っているように、いくつかのコードを見ずに見分けるのは難しいでしょう。

于 2009-06-18T02:06:16.183 に答える
0

コードを見ずに原因を言うのは難しいです。ただし、一般的な問題は再帰的な参照です。オブジェクトAはオブジェクトBを指しており、その逆の場合、GCが失敗する可能性があります。

現在ファイルをどのように処理しているかはわかりませんが、一度に1行だけファイルを読み取ろうとすることができます。ファイル全体を一度に読み取ると、より多くのメモリを消費する可能性があります。

これが、私がバッチ処理タスクにPythonを好む理由の1つです。

于 2009-06-18T01:58:54.247 に答える
0

php.iniでmemory_limitを変更できますか?

また、変数に対してunset($ var)を実行すると、メモリが解放されますか?$ var = nullも役に立ちますか?

この質問も参照してください:PHPでメモリを解放するのに優れている点:unset()または$ var = null

于 2009-06-18T02:16:34.337 に答える
0

私も同じ問題を抱えていましたが、これもデータベースのプロファイリング (Zend_Db_Profiler_Firebug) が原因でした。私の場合、毎分1MBのリークがありました。このスクリプトは数日間実行されるはずだったので、数時間以内にクラッシュします。

于 2010-10-28T02:21:41.753 に答える