将大型数据库导出到没有mysqldump的文件,内存占用量低

时间:2012-09-30 17:50:34

标签: php mysql

  

可能重复:
  memory usage export from database to csv in php

我正在导出一个大型数据库,并想知道使其占用内存较少的最佳方法。

我意识到我必须在有时间限制且使用内存不足的循环中执行此操作,一次取出100行并将信息保存到文件然后重定向以从完成时开始新的循环开始上一个周期。

我想知道最好的方法是将数据缓冲到文件而不是耗尽内存,目前脚本将所有数据作为字符串获取,然后在完成从数据库获取所有行时保存到文件中。有时它会耗尽内存,因此需要修复。

我是否对从数据库中提取的数据使用fwrite()而不是放入var或使用临时文件?如果我在合并/重命名到备份文件时使用临时文件?

基本上,脚本将数据库数据导出到文件中的最佳方法是什么,而不会出现“致命错误:PHP允许的内存大小耗尽”错误?

    function backup_tables($host, $user, $pass, $db, $tables = '*')
    {
            set_time_limit(0);

            $mysqli = new mysqli($host,$user,$pass, $db);
            if ($mysqli->connect_errno)
            {
                echo "Failed to connect to MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
            }

            $return = '';

            $return .= "--\n";
            $return .= "-- Database: `$db`\n";
            $return .= "--\n\n";
            $return .= "-- --------------------------------------------------------\n\n";

            $numtypes = array(
                'tinyint', 
                'smallint',
                'mediumint',
                'int',
                'bigint',
                'float',
                'double',
                'decimal',
                'real'
            );

            // get all of the tables
            if ($tables == '*')
            {
                    $tables = array();
                    $result = $mysqli->query('SHOW TABLES');
                    while ($row = $result->fetch_row())
                    {
                            $tables[] = $row[0];
                    }

                    $result->close();
            }
            else
            {
                    $tables = is_array($tables) ? $tables : explode(',',$tables);
            }

            for ($z = 0; $z == 0; $z++)
            {
                echo $z.'<br>';

            // cycle through tables
            foreach ($tables as $table)
            {
                    //
                    $typesarr = array();
                    $result = $mysqli->query("SHOW COLUMNS FROM `".$table."`");

                    while ($row = $result->fetch_assoc())
                    {
                            $typesarr[] = $row;
                    }
                    $result->close();

                    #echo '<h2>'.$table.'</h2>';
                    #print("<pre>" . print_r($typesarr, true). "</pre>");

                    // table structure dump
                    $return .= "--\n";
                    $return .= "-- Table structure for table `$table`\n";
                    $return .= "--\n\n";                        
                    $return.= 'DROP TABLE IF EXISTS `'.$table.'`;'."\n\n";
                    $result = $mysqli->query("SHOW CREATE TABLE `".$table."`");
                    $row = $result->fetch_array();
                    $return.= $row[1].";\n\n";
                    $result->close();

                    // table data dump
                    $return .= "--\n";
                    $return .= "-- Dumping data for table `$table`\n";
                    $return .= "--\n\n";

                    $result = $mysqli->query("SELECT * FROM `".$table."`");
                    $num_fields = $result->field_count;

                    if ($result->num_rows > 0)
                    {
                            // put field names in array and into sql insert for dump
                            $fields_str = '';
                            $fields =  array();
                            $finfo = $result->fetch_fields();

                            foreach ($finfo as $val)
                            {
                                    $fields_str .= '`'.$val->name.'`, ';
                                    $fields[] = $val->name;
                            }                                

                            $fields_str = '('.rtrim($fields_str, ', ').')';
                            $return.= 'INSERT INTO `'.$table.'` '.$fields_str.' VALUES'."\n";

                            // cycle through fields and check if int for later use
                            for ($i = 0; $i < $num_fields; $i++) 
                            {
                                    // strip brackets from type
                                    $acttype = trim(preg_replace('/\s*\([^)]*\)/', '', $typesarr[$i]['Type']));
                                    $acttype = explode(' ', $acttype);

                                    // build array, is field int or not
                                    if (is_numeric(array_search($acttype[0], $numtypes)))
                                    {
                                            $numflag[$i] = 1;
                                    }
                                    else
                                    {
                                            $numflag[$i] = 0;        
                                    }
                            }
                    }  

                    $x = 0;
                    $num_rows = $result->num_rows;

                    // cycle through table rows
                    while($row = $result->fetch_row())
                    {
                            $x++;

                            // cycle through rows fields
                            for($j=0; $j<$num_fields; $j++) 
                            {          
                                    if (isset($row[$j]) and $j === 0) { $return .= '('; }

                                    // field data has value or not NULL
                                    if (isset($row[$j]))
                                    { 
                                            // field data dump (INT)
                                            if ($numflag[$j]==1)
                                            {
                                                    #echo '(INT) '. $fields[$j].' = '.$row[$j].'<br>';
                                                    $return.= $mysqli->real_escape_string($row[$j]);
                                            } 
                                            else
                                            {
                                                    // field data dump values (empty string, NULL and INT)
                                                    $return.= "'".$mysqli->real_escape_string($row[$j])."'";
                                                    #echo $fields[$j]." = '".$mysqli->real_escape_string($row[$j])."'<br>";
                                            }
                                    }
                                    else
                                    {
                                            // field data dump (NULL)
                                            if (is_null($row[$j]))
                                            {
                                                    $row[$j] = 'NULL';
                                                    #echo '(NULL) '. $fields[$j].' = '.$row[$j].'<br>';
                                                    $return.= $row[$j]; 
                                            }
                                            else
                                            {
                                                    // field data dump (empty string)
                                                    $return.= "''";
                                            }
                                    }

                                    if ($j<($num_fields-1)) { $return.= ', '; }
                            }

                            if ($x<$num_rows) { $return.= "),\n"; } else { $return .= ");\n"; }

                            #echo '<br>';
                    }
                    #echo 'Rows: '.$rows.'<br>';
                    #echo 'Iterations: '.$x.'<br>';
                    $return.="\n-- --------------------------------------------------------\n\n";
            }

            }

            $result->close();

            //save file
            $handle = fopen('/db-backup-'.time().'.sql','a');
            fwrite($handle,$return);
            fclose($handle);
    }

欢迎举例

2 个答案:

答案 0 :(得分:0)

默认情况下,mysqldump的最新版本启用了--opt,其中包括--quick,后者表示一次一个地提取行,而不会在内存中缓冲整个表。因此mysqldump的内存占用量已经非常低了。

使用mysqldump时,您确定确实存在任何问题吗?如果是这样,他们如何表现出来? mysqldump是内存不足,还是mysql服务器,还是其他什么东西?

答案 1 :(得分:0)

MYSQLI_USE_RESULT作为resultmode参数添加到mysqli::query调用,以便一次迭代一行结果,而不是将它们全部转移到PHP中。另请参阅mysqli::use-result的文档。

将每一行直接写入输出文件,避免使用$result变量。结合上述情况,这可能导致从服务器获取每一行并写入文件,因此PHP不必一次存储多行。