1

可能重复:
在 php 中将内存使用情况从数据库导出到 csv

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

我意识到我必须在有时间限制并使用低内存的周期中执行此操作,一次获取 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);
    }

欢迎举例

4

2 回答 2

0

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

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

于 2012-09-30T21:31:26.730 回答
0

mysqldump默认情况下,最新版本已--opt启用,其中包括--quick依次指示一次获取一个行,而不在内存中缓冲整个表。所以已经的内存占用mysqldump应该很低。

您确定使用时实际上有任何问题mysqldump吗?如果是这样,它们是如何表现出来的?是mysqldump内存不足,还是mysql服务器,或者完全是别的什么?

于 2012-09-30T19:19:15.583 回答