Search code examples
phpmysqlfopenfwrite

PHP split fwrite to conserve memory


I'm currently using a script from http://davidwalsh.name/backup-mysql-database-php to do a "hotcopy" like backup of a myisam sql database but the issue im running into is that I get a fatal error with running out of memory.

I'm working with HUGE databases so it writes entire dbs into a string then output the entire string into fwrite at once and I believe thats where the problem lays. So is it possible to split the write action into chunks so I wouldn't run into the out of memory fatal error? If so, how would you achieve this?

Here is my attempt:

ini_set('memory_limit','3000M');
//create the file to be written into
$return = '';
$f = 'db-backup-'.$name.'_'.time().'.sql'; 
$handle = fopen($f,'w+');
fwrite($handle,$return);
fclose($handle);

//...mysql connect goes here then...
//cycle through
foreach($tables as $table)
{
  $result = mysql_query('SELECT * FROM '.$table);
  $num_fields = mysql_num_fields($result);

  $return.= 'DROP TABLE '.$table.';';
  $row2 = mysql_fetch_row(mysql_query('SHOW CREATE TABLE '.$table));
  $return.= "\n\n".$row2[1].";\n\n";

  for ($i = 0; $i < $num_fields; $i++) 
  {
    while($row = mysql_fetch_row($result))
    {
      $return.= 'INSERT INTO '.$table.' VALUES(';
      for($j=0; $j<$num_fields; $j++) 
      {
        $row[$j] = addslashes($row[$j]);
        $row[$j] = ereg_replace("\n","\\n",$row[$j]);
        if (isset($row[$j])) { $return.= '"'.$row[$j].'"' ; } else { $return.= '""'; }
        if ($j<($num_fields-1)) { $return.= ','; }
    }
    $return.= ");\n";
  }
}
$return.="\n\n\n";

//Write each table into file
fopen($f,'w+');
fwrite($handle,$return);
fclose($handle);
$return ='';  //clear return thus clearing memory?
}

I know there are better ways to backup the databases, this is just temporary until I get LVM to work.

Thanks

TL;DR fwrite giving out of memory error, if split the writes will avoid that issue?

ps. mysqldump locks the dbs so i cant use dump, and replication is broken since we are using temporary tables and temp tables are a must.


Solution

  • change this

    $result = mysql_query('SELECT * FROM '.$table);
    

    to

    $result = mysql_query('SELECT * FROM LIMIT 5000'.$table); //this will only get the first 5000.
    

    after you get them and saved you can get the rest by using

    $result = mysql_query('SELECT * FROM OFFSET 5000'.$table);  //you can use limit over here too...