如何在 Yii 框架或 PHP 中解决内存泄漏,除了设置内存限制的指令


How to solve memory leak in Yii framework or PHP, aside from setting the directive of memory limit?

我编写了这个函数来使用 Yii 框架命令行生成一个 CSV 文件现在的问题是我有超过 300,000 行数据要写入 CSV 文件。所以我所做的是,我为每个CSV文件将数据分成10,000个。

  public function Targets2($offset,$limit,$batch){
    if($offset == ''){
        echo 'provied an offset!';
        exit;
    }
    if($limit == ''){
        echo 'provide a limit';
        exit;
    }
    if($batch == ''){
        echo 'you wil overwrite the 1st batch!';
        exit;
    }


    $dataProvider=new CActiveDataProvider('users',
            array(
                    'criteria' => array(
                            'order' => 'userid ASC',
                            'offset'=>$offset,
                            'limit' => $limit,
                    ),
                    'pagination' => false
            )
    );
    $datas = $dataProvider->getData();

    $userdatas = array();
    foreach($datas as $data){
        $h = '"D"';
        $ssn = '""';
        $email = '';
        $mobile_no = '';
        $specialtargetname = '"special target"';
        if(!is_null($data->USEREMAILADR) && !empty($data->USEREMAILADR)){
            $email = '"'.$data->USEREMAILADR.'"';
        } else {
            $email = '""';
        }
        if(!is_null($data->USERCT) && !empty($data->USERCT)){
            $mobile_no = '"'.$data->USERCT.'"';
        } else {
            $mobile_no = '""';
        }
        $userdatas[] = array('"'.$data->USERID.'"',$h,'""',$email,$mobile_no,$specialtargetname);
    }

    $headers = array(
            '"USERID"',
            '"H"',
            '"SSN"',
            '"EMAIL"',
            '"MOBILE_NO"',
            '"SPECIAL_TARGET_NAME"'
    );
    $date = date('Ymd',strtotime('today'));
    $totaldata = count($userdatas);
    $file = 'C:'xampp'htdocs'yii'branch'protected'data'Target_'.$date.'_'.$batch.'.csv';
    $fp = fopen($file, 'w+');
    fputcsv($fp,$headers,','," ");
    if($totaldata > 0){
    foreach($userdatas as $line){
        fputcsv($fp,$line,','," ");
    }
    $end = array('"T"');
    $fp2 = fopen($file,'a+');
    fputcsv($fp2,$end,','," ");
    echo "offset = $offset with limit $limit contacts file generated batch  " . $batch ."'n";       
    fclose($fp);
    fclose($fp2);
    $offset = $limit;
    $limit += 10000;
    $batch += 1;    
    $this->Targets2($offset,$limit,$batch);
    }

现在的问题是,在脚本写入第二批CSV文件之前,就会发生错误

Fatal error: Allowed memory size of 1744830464 bytes exhausted (tried to allocat
e 48 bytes) in C:'xampp'htdocs'yii1.1.10'framework'db'CDbCommand.php on line 506

即使我将memory_limit设置为 1664M,例如

ini_set('memory_limit','1664M');

我仍然收到致命的内存泄漏错误。

如果我将其设置为 "-1"它会使服务器崩溃,我不想在生产中推送该代码。太可怕了。

如何解决或优化?

处理大量数据时,最好避免使用 CActiveRecord 实例,每个实例都会使用附加内存,使用纯数组会有所帮助。 此外,您还需要按如下方式查询块中的数据:

class ImportCommand extends CConsoleCommand
{
  public function run($args)
  {
      $db  = Yii::app()->db;
      $limit     = 1000;  
      $totalRows = $db->createCommand()
                      ->from("users")
                      ->select("count(*)")->queryScalar();
      $n = ceil($totalRows / $limit);
      $k = 0;
      // $fp = fopen(...)
      for($i = 0; $i < $n ; $i++)
      {
          $offset = $i*$limit;
          $list  = $db->createCommand()->from("users")
                   ->offset($offset)
                   ->limit($limit)->queryAll();
          foreach($list as $user)
          {
              //process $user here
          }
      }
      // fclose($fp)
  }
}

此外,如果在查询生成器上使用 select 方法仅指定所需的字段,则可以节省更多内存。