2012-04-10 10 views
0

mongodbから.csvにいくつかのドキュメントをエクスポートしようとしています。いくつかの大規模なリストについては、ファイルは40Mのようなものになるだろう、私は、メモリの制限に関するエラーを取得:メモリサイズが使い果たされました。mongodbからのエクスポートエラー

Fatal error: Allowed memory size of 134217728 bytes exhausted 
(tried to allocate 44992513 bytes) in 
/usr/share/php/Zend/Controller/Response/Abstract.php on line 586 

このエラーはなぜ起こるか、私は疑問に思います。そのような量のメモリを消費するのは何ですか? memory_limitが変更されずにこのようなエラーが発生するのを避けるにはどうすればいいですか?128Mになりました。

私はこのようなものを使用

public static function exportList($listId, $state = self::SUBSCRIBED) 
{ 
     $list = new Model_List();  
     $fieldsInfo = $list->getDescriptionsOfFields($listId); 
     $headers = array(); 
     $params['list_id'] = $listId; 
     $mongodbCursor = self::getCursor($params, $fieldsInfo, $headers); 
     $mongodbCursor->timeout(0); 
     $fp = fopen('php://output', 'w');  
     foreach ($mongodbCursor as $subscriber) { 
      foreach ($fieldsInfo as $fieldInfo) {   
       $field = ($fieldInfo['constant']) ? $fieldInfo['field_tag'] : $fieldInfo['field_id']; 
       if (!isset($subscriber->$field)) { 
        $row[$field] = ''; 
       } elseif (Model_CustomField::isMultivaluedType($fieldInfo['type'])) { 
        $row[$field] = array();  
        foreach ($subscriber->$field as $value) { 
         $row[$field][] = $value;       
        } 
        $row[$field] = implode(self::MULTIVALUED_DELEMITOR, $row[$field]); 
       } else { 
        $row[$field] = $subscriber->$field; 
       }        
      }    
      fputcsv($fp, $row);         
     }     
} 

その後、私のコントローラで、私はこのような何か、それを呼び出そう:だから私は、私はエクスポートファイルの末尾によ

public function exportAction() 
{ 

    set_time_limit(300); 


    $this->_helper->layout->disableLayout(); 
    $this->_helper->viewRenderer->setNoRender(); 
    $fileName = $list->list_name . '.csv'; 

    $this->getResponse()->setHeader('Content-Type', 'text/csv; charset=utf-8') 
         ->setHeader('Content-Disposition', 'attachment; filename="'. $fileName . '"');                

    Model_Subscriber1::exportList($listId); 
    echo 'Peak memory usage: ', memory_get_peak_usage()/1024, ' Memory usage: ', memory_get_usage()/1024; 

} 

データ。それは私が1Mドキュメントのようなものをエクスポートするリストについては、それが成功して表示輸出していることではなく奇妙です:

> Peak memory usage: 50034.921875 Kb Memory usage: 45902.546875 Kb 

しかし、私は1.3M文書をエクスポートしようとすると、その後、数分後に私は、エクスポートファイルで入手:

Fatal error: Allowed memory size of 134217728 bytes exhausted 
(tried to allocate 44992513 bytes) in 
/usr/share/php/Zend/Controller/Response/Abstract.php on line 586. 

エクスポートするドキュメントのサイズはほぼ同じです。私は256Mまでのmemory_limitを増加させ、1.3Mのリストをエクスポートしようとした

、これはそれが示したものです:

Peak memory usage: 60330.4609375Kb Memory usage: 56894.421875 Kb.

それは私にとって非常に混乱しそうです。このデータは不正確ではありませんか?それ以外の場合、memory_limitが128Mに設定されているとメモリが枯渇した原因となるのはなぜですか?

答えて

1

ドキュメントのサイズはほぼ同じですが、PHPで処理するためにPHPによって割り当てられるサイズは、ドキュメントのサイズやドキュメントの数に直接比例しません。これは、PHPで異なるタイプのメモリを割り当てる必要があるためです。あなたが行くにつれていくらかのメモリを解放することができるかもしれませんが、私はあなたのコードのどこにも見当たりません。

ベストアンサーはおそらくメモリ制限を大きくすることです。

あなたができることは、処理を外部スクリプトにオフロードし、PHPから呼び出すことです。多くの言語は、PHPよりもメモリ効率の良い方法でこの種の処理を行います。

私はまた、memory_get_peak_usage()が必ずしも正確ではないことに気付きました。私はmem_limitを256にしてより大きなデータセット(130万)で実行する実験を試みます。あなたはそれが128制限の下でも報告されていることが分かります。

memory_get_usage()で示したが、同じ致命的なエラーになってしまったように私は、私のシステム 十分なメモリを持っているはずCSVファイルをエクスポート、同様の場合には、この問題を再現することができ
+0

メモリを解放する方法もありません。このケースでは、輸出用に他の言語を使用するのはむしろ問題になると思います。メモリ制限を256Mまで試してみるつもりですが、この制限は私にとっては非常に大きいようです。 – Oleg

+0

私は256Mにmemory_limitを増やし、1.3Mのリストをエクスポートしようとしましたが、これはそれが示したものです:ピークメモリ使用量:60330.4609375Kbメモリ使用量:56894.421875 Kb。それは私にとって非常に混乱しているようです。 – Oleg

+0

あなたはこれを説明してください: "あなたができることの1つは、処理を外部スクリプトにオフロードし、PHPから呼び出すことです。"私は今この操作をオンラインで実行します。私はPHPから呼び出される他の言語スクリプトをどのように実行するのかよく分かりません。それはどんな言語ですか? – Oleg

0

致命的なエラー:許可されたメモリサイズ

私はCSVの内容を物理的な一時ファイルに出力することでこの問題を回避しました。 私はループの中でファイルを書きました。それぞれの反復が限られたデータのチャンクしか書いていないので、私は決してメモリの制限を超えませんでした。 圧縮した後、圧縮率は、私が最初に壁に当たったサイズの10倍を超える生ファイルを処理できるほどでした。すべて上がって、それは成功だった。

ヒント:アーカイブを作成するときに、のリンクを外してください。 $ zip-> close()を呼び出す前にアーカイブコンポーネントをアンリンクしないでください。この呼び出しはビジネスを行っているようです。そうしないと、空のアーカイブになります!

コードサンプル:

<?php 
$zip = new ZipArchive; 
if ($zip->open($full_zip_path, ZipArchive::CREATE) === TRUE) { 
    $zip->addFile($full_csv_path, $csv_name); 
    $zip->close(); 

    $Response->setHeader("Content-type", "application/zip; charset=utf-8"); 
    $Response->setHeader("Content-disposition", "attachment; filename=" . $zip_name); 

    $Response->setBody(file_get_contents($full_zip_path)); 
} 
else { 
    var_dump(error_get_last()); 
    echo utf8_decode("Couldn't create zip archive '$full_zip_path'."), "\r\n"; 
} 

unset($zip); 
?> 

は注意:zipアーカイブにアイテムを追加するときに、WindowsベースのOSを使用している場合、項目名の先頭のスラッシュを付加しません。オリジナル問題をめぐる

ディスカッション:

引用されたラインでZendのファイルは、Zend_Controller_Response_AbstractクラスのoutputBody()メソッドから

public function outputBody() 
{ 
    $body = implode('', $this->_body); 
    echo $body; 
} 

です。

しかし、あなたがそれを行う、のようにそれは、、または印刷をエコー、またはのReadFileを通じて、出力は常にキャプチャーされ、あなたのターンの応答リターンがオフ機能も、レスポンスボディに立ち往生、見えます派遣前に$対応 - >のclearBody()続い

私ものclearBodyを(使用しようとした)クラスメソッド、内の各$対応 - > sendResponse(ことに注意して用いて、ループエコー)メモリを解放しますが、失敗しました。 Zendが応答の送信を処理する方法は、生のCSVファイルのフルサイズのメモリ割り当てが常に得られるようなものです。

しかし、出力バッファを「キャプチャ」しないようにZendに指示することは可能でしょうか。

関連する問題