Mysql data export script [for PHP] update 2014/1/5
English article : http://translate.google.com/translate?sl=zh-CN&tl=en&js=n&prev=_t&hl=zh-CN&ie=UTF-8&u=http://www.cnblogs.com/xiaosan/p/3467953.html
更新的地方:
一. 修复对大数据表导出错误。ps : 理论上依着PHP不出问题的话,可以导出无限量数据, 但不包括表的设计逻辑上的完善考虑。
二. 优化对大数据表的导出速度。ps : 具体参考下面提供的资料
三. 增加个性化输出风格.
四. 多个表导出(详见程序注释)
说明:
一. 导出是单个csv 数据文件, 此类型文件结构简单,非常容易进行转换。但由于是单个文件,在数据庞大的时候,就会有另外的体验问题,如果有网友提的话,那我就修改一下!:)
二. 历史数据文件,在有必要的情况请删除或重命名,那是因为程序会通过此判断跳过已经导出的表。
三. 海量数据导出,应调整相应参数来改善效率,详细请见下面解释.
设计简要:
一)增加了浏览器模式,也就是说,该脚本之前是适合在命令终端运行,但有些情况可能在webshell中无法执行命令,所以就增加了浏览器支持。浏览器导出模式是将导出的数据分批导出,采用了javascript 进行前端持续维持导出状态,所以使用的时候javascript 支持也要打开。
二)浏览器模式中“Restart” 链接可以让程序重新加载,再动态对本脚本参数修改时,因此可以更加方便的响应你的修改。
三)在程序代码中“$dump_max_limit” 变量是控制浏览器模式分批导出最大行数,本身这个值是一个猜想值,默认是50000, 你可以修改成500000. 这都是是没问题的, 导出行数越多就越快,只要web server 接受!适当修改影响到导出整体时间短长。此分批设计是为了迎合无限延伸的海量数据,MySQL创建的MyISAM表默认最大尺寸为4G, 所以在这个层次上再庞大的数据,有了这个设计, 应该也是没问题的。
使用预览:
终端模式
浏览器模式
几个虚设的问题(是关键的问题):
- 问 1. 网上很多程序,为什么你还要写这个程序?
- 答 :像很多提供的导出程序, 根本上出现了很多BUG, 导出也不完整,不经意间漏掉了很多宝贵的数据。包括更新了很多个版本的adminer, 数据遗漏也没有对应提示。
- 问 2. 有支持多种数据库导出的脚本吗?
- 答:这个确实有必要写一个,但我不是专业的程序员,写的也不是很专业,但我喜欢分享,我期望自己以后抽出时间写出来,但不是明天早上!之所以这样,是为了出现BUG,制造出了一些不会写程序朋友而言的麻烦,我非常不愿意这样,所以就要等到有大量时间来进行。(声明:时间不是借口!)
- 问 3. 你是谁?
- 答:和谐小组(H3xIe Security Team)成员. 另外你不管我是谁,你能够在茫茫人海中看到我的博文,说明我的标题很符合你的搜索规范!记住,这是幽默句,听了后,心里请不要浮躁----我们讨论技术好不好!
- 问 4. 我的webshell 不能执行命令,那这个脚本不能用在浏览器上访问导出吗?
- 答:我就想到这个鸡肋的情况,也被这个扯伤了。请在脚本中修改参数“browser_mode”为true , 就可以支持浏览器访问了。
- 问 5. 我该采用哪种模式进行数据导出?可以说一下数据导出经验吗?
- 答:在默认情况最好采用终端命令行导出, 因为速度更快, 更不会被web server 配置所限制。如果碰到无法执行命令的情况,那你就用浏览器模式,同时你碰到数据导出不完整的时候,欢迎在我博客反馈。
附件:
https://files.cnblogs.com/xiaosan/output.zip
预览:
<?php /**/{}/**//**/{}/**//**/{}/**//**/{}/**//**/{}/**//**/{}/**//**/{}/**//**/{}/**//**/{}/**//**/{}/**/ #### ##### ######## ####### ### ###### ##### #### #### ######### ####### ##### ##### ##### #### ## #### ######### ## ######## ######## ####### ###### ## ######## ######## ###### ##### ####### ######### ### #### HeXI3 ####### Security ## #### #### TeAm ## ## ##### ### ## ####### ##### ###### ### ##### #### ######### ####(2005 - 2014) #### ###### ####### ###### ##### ##### ########## #### ####### ############# ###### ###### ##### ####### ##### ###### ######## ###### /**/{}/**//**/{}/**//**/{}/**//**/{}/**/ {}/**//**/{}/**//**/{}/**//**/{}/**//**/{}/**//**/{} define('browser_mode', false); /* 注意:如果无法执行命令, 请使用浏览器模式。(改为true 即可) */ define('output_csv_filename', 'data'); /* 便于记忆, 为生成文件名附加以下字符 默认为: data */ define('db_charset', 'gbk'); /* 数据库编码 */ $db_host = 'localhost'; /* 主机名/IP - host */ $db_user = 'test'; /* 数据库用户名 - mysql user */ $db_pass = 'test'; /* 数据库密码 - mysql password */ $db_database = 'test'; /* 数据库名称 - database name */ $table_names = 'test'; /* 可导出多个表,用逗号分隔 */ # // /* 提醒 :程序在“浏览器模式”执行中途,不要修改参数,这样会影响到程序正确取值。*/ // 使用介绍(海量数据导出须知,经验浅谈,注意事项,使用说明) : http://www.cnblogs.com/xiaosan/p/3467953.html # # 浏览器模式参数 { /* 浏览器模式 导出支持 参数 */ $limit = 0; /* 此参数为浏览器模式提供 */ $dump_max_limit = 50000; $index_table = 0; $time_start = 0; $Br_str = '<br />'; } $msg_arr = array('dump_succ' => 'Operation has completed!', 'exist_table' => "Skip the table \"%s\", this directory already exists \"%s\".", 'not_browser_mode' => "program setting not browser mode", 'exist_dbfile' => "This directory already exists \"%s\"", 'dump_wait' => "Dump Table : \"%s\" waiting...", 'dump_status' => " - Dump Index : %s", 'not_terminal_mode' => "program setting not terminal mode", 'export_table_succ' => "Export %s complete, csvfile = %s. (%f sec)", 'browser_dump_succ' => 'Operation has completed! (%f sec)' ); /* 系统参数 提示:特殊情况修改,否则默认即可 */ /* ********************************************************** */ ini_set('max_execution_time', 0); ignore_user_abort(); ini_set('memory_limit','1024M'); // 内存最大空间。 @header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); @header("Cache-Control: no-cache, must-revalidate"); @header("Pragma: no-cache"); /* ********************************************************* */ if (browser_mode) { if (!isset($_SERVER["HTTP_HOST"])) {msg('no_wrap_exit', $msg_arr['not_terminal_mode']);Exit;} ob_flush(); flush(); if (isset($_GET['limit'])) { if (is_numeric($_GET['limit'])) { $int_get_limit = $_GET['limit']; $limit = $int_get_limit; # limit 限制值累计增加 } } echo '<hr />'; echo 'Menu : <a href="'.basename(__FILE__).'"><h1>Restart</h1></a>'; echo '<hr />'; } else { $Br_str = ''; if (isset($_SERVER["HTTP_HOST"])) {echo '<h1>Error : '.$msg_arr['not_browser_mode'].'</h1>';Exit;} } $out_csvfile = ''; tables_output(); # run function tables_output() { GLOBAL $table_names, $out_csvfile, $msg_arr; GLOBAL $index_table, $time_start; GLOBAL $limit, $dump_max_limit; $tables_arr = explode(',', $table_names); if (browser_mode) # 浏览器模式 { if (isset($_GET['b_time'])) { $time_start = $_GET['b_time']; }else $time_start = microtime_float();; $name = $tables_arr[0]; # 初始化 if (isset($_GET['tb_index'])) { $index_table = $_GET['tb_index']; if (isset($tables_arr[$index_table])) { $name = $tables_arr[$index_table]; }else { $time_end = microtime_float(); $time = $time_end - $time_start; msg('msg', $msg_arr['browser_dump_succ'], $time); ls_show(); Exit; } } $out_csvfile = $name.'_'.output_csv_filename.'_'.@date('Y-m-d').'.csv'; if ( ($limit == 0) && is_file($out_csvfile)) { msg('msg', $msg_arr['exist_table'], $name, $out_csvfile); $index_table++; Browser_session_continue($limit, $index_table, '', 4); } msg('msg', 'Mode : browser.'); msg('msg', 'Max Dump Limit ('.$dump_max_limit.')'); msg('msg', 'Dump Limit -> '.$limit); msg('msg', '<font color="red">Please wait...</font>'); export_table($name); Exit; } # 常规模式 foreach($tables_arr as $name) { $name = Trim($name); $out_csvfile = $name.'_'.output_csv_filename.'_'.@date('Y-m-d').'.csv'; if (is_file($out_csvfile)) { msg('msg', $msg_arr['exist_table'], $name, $out_csvfile); Continue; } msg('msg', $msg_arr['dump_wait'], $name); $time_start = microtime_float(); export_table($name); $time_end = microtime_float(); $time = $time_end - $time_start; msg('err', $msg_arr['export_table_succ'], $name, $out_csvfile, $time); } ls_show(); if (!browser_mode) msg('msg', $msg_arr['dump_succ']); } function microtime_float() { list($usec, $sec) = explode(" ", microtime()); return ((float)$usec + (float)$sec); } function msg() { GLOBAL $Br_str; $args = func_get_args(); if ($args[0] == 'no_wrap_exit') { $args[0] = 'exit'; $Br_str = ''; } $paramter_s = ''; if ((!isset($args[2])) && $args[0] == 'msg') {echo '[m] '.$args[1]."\n".$Br_str;return;} if ((!isset($args[2])) && $args[0] == 'err') {echo "[e] $args[1].\n".$Br_str;} if ((!isset($args[2])) && $args[0] == 'exit') { die("[e+] $args[1].\n".$Br_str);} foreach($args as $value) { if (($args[0] != $value) && ($args[1] != $value)) $paramter_s .= '\''.$value.'\','; } $paramter_s = substr($paramter_s, 0, -1); if ($args[0] == 'msg') eval("echo '[m] '.sprintf('$args[1]', ".$paramter_s.").\"\\n\".'".$Br_str."';"); elseif ($args[0] == 'exit') eval("echo '[e+] '.sprintf('$args[1]', ".$paramter_s.").\"\\n\".'".$Br_str."';Exit;"); elseif ($args[0] == 'err') eval("echo '[e] '.sprintf('$args[1]', ".$paramter_s.").\"\\n\".'".$Br_str."';"); elseif ($args[0] == 'status') eval("echo ' '.sprintf('$args[1]', ".$paramter_s.").\"\\n\".'".$Br_str."';"); } function Browser_session_continue($limit, $table_index = 0, $status = '', $sleep = 1) { GLOBAL $time_start; $query_str = ''; if (isset($limit) && $table_index != 0) { $query_str = 'limit='.$limit.'&tb_index='.$table_index; } elseif (isset($limit) && $table_index == 0) { $query_str = 'limit='.$limit; } if (!isset($_GET['b_time'])) $query_str .= '&b_time='.$time_start; else $query_str .= '&b_time='.$_GET['b_time']; echo '<meta http-equiv="refresh" content="'.$sleep.'; url=?'.$query_str.'" />'; Exit; } function ls_show() { GLOBAL $Br_str; $self_dir = @scandir('.'); $ls = ''; if (!is_array($self_dir)) return; if (count($self_dir) == 0) return; foreach($self_dir as $now) { if (substr($now, -4) == '.csv') $ls .= ' -'.' '.$now.' Size(MB) : '.(filesize($now) / 1024 / 1024)."\n".$Br_str; } if ($ls != '') { echo '[s] ls *.csv'."\n".$Br_str; echo $ls.$Br_str; echo "\n".$Br_str; } } function export_table($table_name) { GLOBAL $db_host, $db_user, $db_pass, $db_database, $out_csvfile; GLOBAL $limit, $index_table; GLOBAL $Br_str; GLOBAL $msg_arr; $sql = @mysql_connect($db_host, $db_user, $db_pass) or msg('exit', mysql_error()); @mysql_select_db($db_database, $sql) or msg('exit', mysql_error()); mysql_query('SET NAMES '.db_charset); # 常规模式变量 { $Dump_limit = 500000; // 分批查询 $limit_i = 0; } if (browser_mode) # 让程序再脚本限制时间内, 尽量把导出任务做好 { # 浏览器模式 GLOBAL $dump_max_limit; $query = "SELECT * FROM `$table_name` ORDER BY 1 DESC LIMIT $limit,$dump_max_limit"; }else # 常规模式 { msg('Mode : default.'); $query = "SELECT * FROM $table_name ORDER BY 1 DESC LIMIT $limit_i,$Dump_limit"; } while($export = mysql_query($query)) { if (!$export) { echo("Sql error : ".mysql_error()."\n".$Br_str);return; } if (browser_mode) { if (!mysql_num_rows($export)) { $index_table++; $limit = 0; Browser_session_continue($limit, $index_table); } } else { $limit_i = $limit_i + $Dump_limit; msg('status', $msg_arr['dump_status'], $limit_i); $query = "SELECT * FROM $table_name ORDER BY 1 DESC LIMIT $limit_i,$Dump_limit"; if (!mysql_num_rows($export)) { msg('status', $msg_arr['dump_status'], $limit_i.' (Next)'); return; } } $header = ''; $data = ''; if ( (!isset($_GET['limit'])) || ($limit_i == $Dump_limit)) # 浏览器模式持续导出操作,不需要此过程 { $fields = mysql_num_fields ($export); for ( $i = 0; $i < $fields; $i++) { $header .= mysql_field_name($export, $i).','; } } $fh = fopen($out_csvfile, 'a+'); if ($header != '') { $header = substr($header, 0, -1); fwrite($fh, $header."\n"); } while ($row = mysql_fetch_array($export, MYSQL_NUM)) { $line = ''; foreach($row as $value) { if ((!isset($value)) || ($value == "")) { $value = '"",'; } else { $value = str_replace('"', '""', $value ); $value = '"'.$value.'"'.','; } $line .= $value; } fwrite($fh, substr(Trim($line), 0, -1)."\n"); } fclose($fh); if (browser_mode) Break; } mysql_close($sql); if (browser_mode) # 生成持续导出会话。(由浏览器自主完成) { $limit = $limit + $dump_max_limit; Browser_session_continue($limit, $index_table); } } ?>