2014-02-13 65 views
0

我得到的主題。當我嘗試在文本文件中備份我的數據庫時。「PHP致命錯誤:允許的內存大小134217728字節耗盡」,同時生成數據庫備份

function backup_tables($backup_filename, $tables = '*') 
{ 
    $conf = new JConfig(); 
    $dbhost = $conf->host; 
    $dbuser = $conf->user; 
    $dbpassword = $conf->password; 
    $dbname = $conf->db; 
    $link = mysql_connect($dbhost, $dbuser, $dbpassword); 
    mysql_select_db($dbname, $link) or die(mysql_error()); 
    $return = "drop database if exists `$dbname`;\n\ncreate database `$dbname`;\n\nuse `$dbname`;\n\n"; 
    $return .= "/*!40101 SET @[email protected]@CHARACTER_SET_CLIENT */;\n\n"; 
    $return .= "/*!40101 SET @[email protected]@CHARACTER_SET_RESULTS */;\n\n"; 
    $return .= "/*!40101 SET @[email protected]@COLLATION_CONNECTION */;\n\n"; 
    $return .= "/*!40101 SET NAMES utf8 */;\n\n"; 

    $handle = fopen($backup_filename, 'w+'); 
    fwrite($handle, $return); $return = ""; 

    // get all of the tables 
    if ($tables == '*') { 
     $tables = array(); 
     $result = mysql_query('SHOW TABLES'); 
     while ($row = mysql_fetch_row($result)) { 
      $tables[] = $row[0]; 
     } 
    } else { 
     $tables = is_array($tables) ? $tables : explode(',', $tables); 
    } 

    // cycle through 
    foreach ($tables as $table) { 
     $result = mysql_query('SELECT * FROM ' . $table); 
     $num_fields = mysql_num_fields($result); 
     $return .= 'DROP TABLE IF EXISTS `' . $table . '`;'; 
     $return .= "\n\n" . mysql_fetch_row(mysql_query('SHOW CREATE TABLE `' . $table . '`;'))[1] . " DEFAULT CHARSET=cp1251;\n\n"; 

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

      fwrite($handle, $return); $return = ""; 
     } 
     if($return) { 
      fwrite($handle, $return); 
      $return .= "\n\n\n"; 
     } 
    } 

    fclose($handle); 
} 

這個函數可以很好的處理內存泄漏的異常。它創建一個〜30 MiB的文件,並提及提及的錯誤。正在進行文件生成時,httpd進程的內存使用量會一致增加。還有一件事:在一張大桌子(包含一個日誌)上生成一代,但我認爲這不是因爲逐行寫入的原因信息。

+0

我建議使用數據庫工具來提取您的SQL數據庫,然後將其導出爲CSV或其他格式。如果你這樣做,它會消耗更多的內存和時間。 – Newbi3

+0

如果你堅持使用這個腳本,我會建議在你的'php.ini'文件中設置內存限制,並且可以修復它。但它比使用專門的數據庫ETL(提取轉換加載)工具要慢很多,這些工具內置在TOAD,Sequel Pro和其他DB工具中,這些工具不是用PHP編寫的,並專門用於此目的。 – Newbi3

+0

在這個項目中使用外部工具是非常不可取的。 –

回答

1

And one more: generation hungs at a large table (containing a log), but I think this is no matter 'cause information written row by row.

其實這是原因:我應該使用mysql_unbuffered_query代替mysql_query。現在這個功能看起來像這樣:

function backup_tables($backup_filename, $tables = '*') 
{ 
    $conf = new JConfig(); 
    $dbhost = $conf->host; 
    $dbuser = $conf->user; 
    $dbpassword = $conf->password; 
    $dbname = $conf->db; 
    $link = mysql_connect($dbhost, $dbuser, $dbpassword); 
    mysql_select_db($dbname, $link) or die(mysql_error()); 
    $return = "drop database if exists `$dbname`;\n\ncreate database `$dbname`;\n\nuse `$dbname`;\n\n"; 
    $return .= "/*!40101 SET @[email protected]@CHARACTER_SET_CLIENT */;\n\n"; 
    $return .= "/*!40101 SET @[email protected]@CHARACTER_SET_RESULTS */;\n\n"; 
    $return .= "/*!40101 SET @[email protected]@COLLATION_CONNECTION */;\n\n"; 
    $return .= "/*!40101 SET NAMES utf8 */;\n\n"; 

    $handle = fopen($backup_filename, 'w+'); 
    fwrite($handle, $return); $return = ""; 

    // get all of the tables 
    if ($tables == '*') { 
     $tables = array(); 
     $result = mysql_query("SHOW TABLES"); 
     while ($row = mysql_fetch_row($result)) { 
      $tables[] = $row[0]; 
     } 
    } else { 
     $tables = is_array($tables) ? $tables : explode(',', $tables); 
    } 

    // cycle through 
    foreach ($tables as $table) { 
     $return .= "DROP TABLE IF EXISTS `$table`;"; 
     $return .= "\n\n" . mysql_fetch_row(mysql_query("SHOW CREATE TABLE `$table`;"))[1] . " DEFAULT CHARSET=cp1251;\n\n"; 

     $result = mysql_unbuffered_query("SELECT * FROM `$table`"); 
     $num_fields = mysql_num_fields($result); 

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

      fwrite($handle, $return); $return = ""; 
     } 
     if($return) 
      fwrite($handle, $return); 

     $return = "\n\n\n"; 
    } 

    fclose($handle); 
} 
+0

感謝您跟進您作爲解決方案實施的內容。總是善於學習一些新的東西。 – mrjandro

0

這裏的PHP答案是增加最大內存大小,如果不是最大執行時間在同一時間。

這是在重新創建mysqldump命令的練習之外,是否有理由在PHP代碼中執行此操作?

使用mysqldump或類似Holland http://hollandbackup.org/的東西可能會更好,可以逐個轉儲每個表。

+0

爲什麼你們所有人都建議增加最大內存?我認爲有必要了解這種低內存錯誤出現的原因(所以我也這樣做了)。 –

+0

簡而言之,由於數據庫查詢佔用大量內存,您的PHP腳本正在創建大量對象。即使您的機器可能有2048MB或者安裝了多少,PHP的默認內存限制是在php.ini文件中設置的,有些限制只有128MB。 此外,通常PHP腳本的默認執行時間爲30秒。 如果你使用了大量的內存,超過128MB和30秒的執行時間,你的腳本將無法完成 使用專用的數據庫工具不依賴於PHP或Apache並直接與數據庫進行交談,明顯更快 – Newbi3

0

當前答案使用deprecated function。新方法是使用mysqli::use_result

在我的情況下,我跑進了耗盡的內存錯誤試圖寫入一個大的sql表到一個文件。以下是我使用它的方式。

$conn = new mysqli("localhost", "my_user", "my_password", "my_db"); 

$sql = 'SELECT row1, row2 from table'; 

$fp = fopen('output.json', 'w'); 
if ($conn->multi_query($sql)) { 
    do { 
     if ($result = $conn->use_result()) { 
      while ($row = $result->fetch_row()) { 
       $row1 = $row[0]; 
       $row2 = $row[1]; 
       $item = array('row1'=>$row1, 'row2'=>$row2); 
       fwrite($fp, json_encode($item)); 
      } 
      $result->close(); 
     } 
    } while ($conn->more_results() && $conn->next_result()); 
} 

fclose($fp); 
$conn->close(); 
相關問題