最近工作中常常需要備份遠程服務器上的Mysql數據庫到本機,一開始采用直接備份Mysql的data目錄的辦法,但由於編碼不同的原因經常產生問題。後來朋友推薦我使用一個非常方便小巧的PHP程序——MyDB。一共包含三個文件:
1. mydb.php //DB類
復制代碼 代碼如下:
<?
class db{
var $linkid;
var $sqlid;
var $record;
function db($host="",$username="",$password="",$database="")
{
if(!$this->linkid) @$this->linkid = mysql_connect($host, $username, $password) or die("連接服務器失敗.");
@mysql_select_db($database,$this->linkid) or die("無法打開數據庫");
return $this->linkid;}
function query($sql)
{if($this->sqlid=mysql_query($sql,$this->linkid)) return $this->sqlid;
else {
$this->err_report($sql,mysql_error);
return false;}
}
function nr($sql_id="")
{if(!$sql_id) $sql_id=$this->sqlid;
return mysql_num_rows($sql_id);}
function nf($sql_id="")
{if(!$sql_id) $sql_id=$this->sqlid;
return mysql_num_fields($sql_id);}
function nextrecord($sql_id="")
{if(!$sql_id) $sql_id=$this->sqlid;
if($this->record=mysql_fetch_array($sql_id)) return $this->record;
else return false;
}
function f($name)
{
if($this->record[$name]) return $this->record[$name];
else return false;
}
function close() {mysql_close($this->linkid);}
function lock($tblname,$op="WRITE")
{if(mysql_query("lock tables ".$tblname." ".$op)) return true; else return false;}
function unlock()
{if(mysql_query("unlock tables")) return true; else return false;}
function ar() {
return @mysql_affected_rows($this->linkid);
}
function i_id() {
return mysql_insert_id();
}
function err_report($sql,$err)
{
echo "Mysql查詢錯誤<br>";
echo "查詢語句:".$sql."<br>";
echo "錯誤信息:".$err;
}
/****************************************類結束***************************/
}?>
2. backup.php //備份腳本
復制代碼 代碼如下:
<?
global $mysqlhost, $mysqluser, $mysqlpwd, $mysqldb;
$mysqlhost="localhost"; //host name
$mysqluser="root"; //login name
$mysqlpwd=""; //password
$mysqldb=""; //name of database
include("mydb.php");
$d=new db($mysqlhost,$mysqluser,$mysqlpwd,$mysqldb);
/*--------------界面--------------*/if(!$_POST['act']){/*----------------------*/
$msgs[]="服務器備份目錄為backup";
$msgs[]="對於較大的數據表,強烈建議使用分卷備份";
$msgs[]="只有選擇備份到服務器,才能使用分卷備份功能";
show_msg($msgs);
?>
<form name="form1" method="post" action="backup.php">
<table width="99%" border="1" cellpadding='0' cellspacing='1'>
<tr align="center" class='header'><td colspan="2">數據備份</td></tr>
<tr><td colspan="2">備份方式</td></tr>
<tr><td><input type="radio" name="bfzl" value="quanbubiao"> 備份全部數據</td><td>備份全部數據表中的數據到一個備份文件</td></tr>
<tr><td><input type="radio" name="bfzl" value="danbiao">備份單張表數據
<select name="tablename"><option value="">請選擇</option>
<?
$d->query("show table status from $mysqldb");
while($d->nextrecord()){
echo "<option value='".$d->f('Name')."'>".$d->f('Name')."</option>";}
?>
</select></td><td>備份選中數據表中的數據到單獨的備份文件</td></tr>
<tr><td colspan="2">使用分卷備份</td></tr>
<tr><td colspan="2"><input type="checkbox" name="fenjuan" value="yes">
分卷備份 <input name="filesize" type="text" size="10">K</td></tr>
<tr><td colspan="2">選擇目標位置</td></tr>
<tr><td colspan="2"><input type="radio" name="weizhi" value="server" checked>備份到服務器</td></tr><tr class="cells"><td colspan='2'> <input type="radio" name="weizhi" value="localpc">
備份到本地</td></tr>
<tr><td colspan="2" align='center'><input type="submit" name="act" value="備份"></td></tr>
</table></form>
<?/*-------------界面結束-------------*/}/*---------------------------------*/
/*----*/else{/*--------------主程序-----------------------------------------*/
if($_POST['weizhi']=="localpc"&&$_POST['fenjuan']=='yes')
{$msgs[]="只有選擇備份到服務器,才能使用分卷備份功能";
show_msg($msgs); pageend();}
if($_POST['fenjuan']=="yes"&&!$_POST['filesize'])
{$msgs[]="您選擇了分卷備份功能,但未填寫分卷文件大小";
show_msg($msgs); pageend();}
if($_POST['weizhi']=="server"&&!writeable("./backup"))
{$msgs[]="備份文件存放目錄'./backup'不可寫,請修改目錄屬性";
show_msg($msgs); pageend();}
/*----------備份全部表-------------*/if($_POST['bfzl']=="quanbubiao"){/*----*/
/*----不分卷*/if(!$_POST['fenjuan']){/*--------------------------------*/
if(!$tables=$d->query("show table status from $mysqldb"))
{$msgs[]="讀數據庫結構錯誤"; show_msg($msgs); pageend();}
$sql="";
while($d->nextrecord($tables))
{
$table=$d->f("Name");
$sql.=make_header($table);
$d->query("select * from $table");
$num_fields=$d->nf();
while($d->nextrecord())
{$sql.=make_record($table,$num_fields);}
}
$filename=date("Ymd",time())."_all.sql";
if($_POST['weizhi']=="localpc") down_file($sql,$filename);
elseif($_POST['weizhi']=="server")
{if(write_file($sql,$filename))
$msgs[]="全部數據表數據備份完成,生成備份文件'./backup/$filename'";
else $msgs[]="備份全部數據表失敗";
show_msg($msgs);
pageend();
}
/*-----------------不要卷結束*/}/*-----------------------*/
/*-----------------分卷*/else{/*-------------------------*/
if(!$_POST['filesize'])
{$msgs[]="請填寫備份文件分卷大小"; show_msg($msgs);pageend();}
if(!$tables=$d->query("show table status from $mysqldb"))
{$msgs[]="讀數據庫結構錯誤"; show_msg($msgs); pageend();}
$sql=""; $p=1;
$filename=date("Ymd",time())."_all";
while($d->nextrecord($tables))
{
$table=$d->f("Name");
$sql.=make_header($table);
$d->query("select * from $table");
$num_fields=$d->nf();
while($d->nextrecord())
{$sql.=make_record($table,$num_fields);
if(strlen($sql)>=$_POST['filesize']*1000){
$filename.=("_v".$p.".sql");
if(write_file($sql,$filename))
$msgs[]="全部數據表-卷-".$p."-數據備份完成,生成備份文件'./backup/$filename'";
else $msgs[]="備份表-".$_POST['tablename']."-失敗";
$p++;
$filename=date("Ymd",time())."_all";
$sql="";}
}
}
if($sql!=""){$filename.=("_v".$p.".sql");
if(write_file($sql,$filename))
$msgs[]="全部數據表-卷-".$p."-數據備份完成,生成備份文件'./backup/$filename'";}
show_msg($msgs);
/*---------------------分卷結束*/}/*--------------------------------------*/
/*--------備份全部表結束*/}/*---------------------------------------------*/
/*--------備份單表------*/elseif($_POST['bfzl']=="danbiao"){/*------------*/
if(!$_POST['tablename'])
{$msgs[]="請選擇要備份的數據表"; show_msg($msgs); pageend();}
/*--------不分卷*/if(!$_POST['fenjuan']){/*-------------------------------*/
$sql=make_header($_POST['tablename']);
$d->query("select * from ".$_POST['tablename']);
$num_fields=$d->nf();
while($d->nextrecord())
{$sql.=make_record($_POST['tablename'],$num_fields);}
$filename=date("Ymd",time())."_".$_POST['tablename'].".sql";
if($_POST['weizhi']=="localpc") down_file($sql,$filename);
elseif($_POST['weizhi']=="server")
{if(write_file($sql,$filename))
$msgs[]="表-".$_POST['tablename']."-數據備份完成,生成備份文件'./backup/$filename'";
else $msgs[]="備份表-".$_POST['tablename']."-失敗";
show_msg($msgs);
pageend();
}
/*----------------不要卷結束*/}/*------------------------------------*/
/*----------------分卷*/else{/*--------------------------------------*/
if(!$_POST['filesize'])
{$msgs[]="請填寫備份文件分卷大小"; show_msg($msgs);pageend();}
$sql=make_header($_POST['tablename']); $p=1;
$filename=date("Ymd",time())."_".$_POST['tablename'];
$d->query("select * from ".$_POST['tablename']);
$num_fields=$d->nf();
while ($d->nextrecord())
{
$sql.=make_record($_POST['tablename'],$num_fields);
if(strlen($sql)>=$_POST['filesize']*1000){
$filename.=("_v".$p.".sql");
if(write_file($sql,$filename))
$msgs[]="表-".$_POST['tablename']."-卷-".$p."-數據備份完成,生成備份文件'./backup/$filename'";
else $msgs[]="備份表-".$_POST['tablename']."-失敗";
$p++;
$filename=date("Ymd",time())."_".$_POST['tablename'];
$sql="";}
}
if($sql!=""){$filename.=("_v".$p.".sql");
if(write_file($sql,$filename))
$msgs[]="表-".$_POST['tablename']."-卷-".$p."-數據備份完成,生成備份文件'./backup/$filename'";}
show_msg($msgs);
/*----------分卷結束*/}/*--------------------------------------------------*/
/*----------備份單表結束*/}/*----------------------------------------------*/
/*---*/}/*-------------主程序結束------------------------------------------*/
function write_file($sql,$filename)
{
$re=true;
if(!@$fp=fopen("./backup/".$filename,"w+")) {$re=false; echo "failed to open target file";}
if(!@fwrite($fp,$sql)) {$re=false; echo "failed to write file";}
if(!@fclose($fp)) {$re=false; echo "failed to close target file";}
return $re;
}
function down_file($sql,$filename)
{
ob_end_clean();
header("Content-Encoding: none");
header("Content-Type: ".(strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') ? 'application/octetstream' : 'application/octet-stream'));
header("Content-Disposition: ".(strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') ? 'inline; ' : 'attachment; ')."filename=".$filename);
header("Content-Length: ".strlen($sql));
header("Pragma: no-cache");
header("Expires: 0");
echo $sql;
$e=ob_get_contents();
ob_end_clean();
}
function writeable($dir)
{
if(!is_dir($dir)) {
@mkdir($dir, 0777);
}
if(is_dir($dir))
{
if($fp = @fopen("$dir/test.test", 'w'))
{
@fclose($fp);
@unlink("$dir/test.test");
$writeable = 1;
}
else {
$writeable = 0;
}
}
return $writeable;
}
function make_header($table)
{global $d;
$sql="DROP TABLE IF EXISTS ".$table."\n";
$d->query("show create table ".$table);
$d->nextrecord();
$tmp=preg_replace("/\n/","",$d->f("Create Table"));
$sql.=$tmp."\n";
return $sql;
}
function make_record($table,$num_fields)
{global $d;
$comma="";
$sql .= "INSERT INTO ".$table." VALUES(";
for($i = 0; $i < $num_fields; $i++)
{$sql .= ($comma."'".mysql_escape_string($d->record[$i])."'"); $comma = ",";}
$sql .= ")\n";
return $sql;
}
function show_msg($msgs)
{
$title="提示:";
echo "<table width='100%' border='1' cellpadding='0' cellspacing='1'>";
echo "<tr><td>".$title."</td></tr>";
echo "<tr><td><br><ul>";
while (list($k,$v)=each($msgs))
{
echo "<li>".$v."</li>";
}
echo "</ul></td></tr></table>";
}
function pageend()
{
exit();
}
?>
3. restore.php //還原腳本
復制代碼 代碼如下:
<?
session_start();
global $mysqlhost, $mysqluser, $mysqlpwd, $mysqldb;
$mysqlhost="localhost"; //host name
$mysqluser="root"; //login name
$mysqlpwd=""; //password
$mysqldb=""; //name of database
include("mydb.php");
$d=new db($mysqlhost,$mysqluser,$mysqlpwd,$mysqldb);
/******界面*/if(!$_POST['act']&&!$_SESSION['data_file']){/**********************/
$msgs[]="本功能在恢復備份數據的同時,將全部覆蓋原有數據,請確定是否需要恢復,以免造成數據損失";
$msgs[]="數據恢復功能只能恢復由dShop導出的數據文件,其他軟件導出格式可能無法識別";
$msgs[]="從本地恢復數據需要服務器支持文件上傳並保證數據尺寸小於允許上傳的上限,否則只能使用從服務器恢復";
$msgs[]="如果您使用了分卷備份,只需手工導入文件卷1,其他數據文件會由系統自動導入";
show_msg($msgs);
?>
<form action="" method="post" enctype="multipart/form-data" name="restore.php">
<table width="91%" border="0" cellpadding="0" cellspacing="1">
<tr align="center" class="header"><td colspan="2" align="center">數據恢復</td></tr>
<tr><td width="33%"><input type="radio" name="restorefrom" value="server" checked>
從服務器文件恢復 </td><td width="67%"><select name="serverfile">
<option value="">-請選擇-</option>
<?
$handle=opendir('./backup');
while ($file = readdir($handle)) {
if(eregi("^[0-9]{8,8}([0-9a-z_]+)(\.sql)$",$file)) echo "<option value='$file'>$file</option>";}
closedir($handle);
?>
</select> </td></tr>
<tr><td><input type="radio" name="restorefrom" value="localpc"> 從本地文件恢復</td>
<td><input type="hidden" name="MAX_FILE_SIZE" value="1500000"><input type="file" name="myfile"></td></tr>
<tr><td colspan="2" align="center"> <input type="submit" name="act" value="恢復"></td> </tr></table></form>
<?/**************************界面結束*/}/*************************************/
/****************************主程序*/if($_POST['act']=="恢復"){/**************/
/***************服務器恢復*/if($_POST['restorefrom']=="server"){/**************/
if(!$_POST['serverfile'])
{$msgs[]="您選擇從服務器文件恢復備份,但沒有指定備份文件";
show_msg($msgs); pageend(); }
if(!eregi("_v[0-9]+",$_POST['serverfile']))
{$filename="./backup/".$_POST['serverfile'];
if(import($filename)) $msgs[]="備份文件".$_POST['serverfile']."成功導入數據庫";
else $msgs[]="備份文件".$_POST['serverfile']."導入失敗";
show_msg($msgs); pageend();
}
else
{
$filename="./backup/".$_POST['serverfile'];
if(import($filename)) $msgs[]="備份文件".$_POST['serverfile']."成功導入數據庫";
else {$msgs[]="備份文件".$_POST['serverfile']."導入失敗";show_msg($msgs);pageend();}
$voltmp=explode("_v",$_POST['serverfile']);
$volname=$voltmp[0];
$volnum=explode(".sq",$voltmp[1]);
$volnum=intval($volnum[0])+1;
$tmpfile=$volname."_v".$volnum.".sql";
if(file_exists("./backup/".$tmpfile))
{
$msgs[]="程序將在3秒鐘後自動開始導入此分卷備份的下一部份:文件".$tmpfile.",請勿手動中止程序的運行,以免數據庫結構受損";
$_SESSION['data_file']=$tmpfile;
show_msg($msgs);
sleep(3);
echo "<script language='javascript'>";
echo "location='restore.php';";
echo "</script>";
}
else
{
$msgs[]="此分卷備份全部導入成功";
show_msg($msgs);
}
}
/**************服務器恢復結束*/}/********************************************/
/*****************本地恢復*/if($_POST['restorefrom']=="localpc"){/**************/
switch ($_FILES['myfile']['error'])
{
case 1:
case 2:
$msgs[]="您上傳的文件大於服務器限定值,上傳未成功";
break;
case 3:
$msgs[]="未能從本地完整上傳備份文件";
break;
case 4:
$msgs[]="從本地上傳備份文件失敗";
break;
case 0:
break;
}
if($msgs){show_msg($msgs);pageend();}
$fname=date("Ymd",time())."_".sjs(5).".sql";
if (is_uploaded_file($_FILES['myfile']['tmp_name'])) {
copy($_FILES['myfile']['tmp_name'], "./backup/".$fname);}
if (file_exists("./backup/".$fname))
{
$msgs[]="本地備份文件上傳成功";
if(import("./backup/".$fname)) {$msgs[]="本地備份文件成功導入數據庫"; unlink("./backup/".$fname);}
else $msgs[]="本地備份文件導入數據庫失敗";
}
else ($msgs[]="從本地上傳備份文件失敗");
show_msg($msgs);
/****本地恢復結束*****/}/****************************************************/
/****************************主程序結束*/}/**********************************/
/*************************剩余分卷備份恢復**********************************/
if(!$_POST['act']&&$_SESSION['data_file'])
{
$filename="./backup/".$_SESSION['data_file'];
if(import($filename)) $msgs[]="備份文件".$_SESSION['data_file']."成功導入數據庫";
else {$msgs[]="備份文件".$_SESSION['data_file']."導入失敗";show_msg($msgs);pageend();}
$voltmp=explode("_v",$_SESSION['data_file']);
$volname=$voltmp[0];
$volnum=explode(".sq",$voltmp[1]);
$volnum=intval($volnum[0])+1;
$tmpfile=$volname."_v".$volnum.".sql";
if(file_exists("./backup/".$tmpfile))
{
$msgs[]="程序將在3秒鐘後自動開始導入此分卷備份的下一部份:文件".$tmpfile.",請勿手動中止程序的運行,以免數據庫結構受損";
$_SESSION['data_file']=$tmpfile;
show_msg($msgs);
sleep(3);
echo "<script language='javascript'>";
echo "location='restore.php';";
echo "</script>";
}
else
{
$msgs[]="此分卷備份全部導入成功";
unset($_SESSION['data_file']);
show_msg($msgs);
}
}
/**********************剩余分卷備份恢復結束*******************************/
function import($fname)
{global $d;
$sqls=file($fname);
foreach($sqls as $sql)
{
str_replace("\r","",$sql);
str_replace("\n","",$sql);
if(!$d->query(trim($sql))) return false;
}
return true;
}
function show_msg($msgs)
{
$title="提示:";
echo "<table width='100%' border='1' cellpadding='0' cellspacing='1'>";
echo "<tr><td>".$title."</td></tr>";
echo "<tr><td><br><ul>";
while (list($k,$v)=each($msgs))
{
echo "<li>".$v."</li>";
}
echo "</ul></td></tr></table>";
}
function pageend()
{
exit();
}
?>
文件結構非常清晰,只要在文件2和3裡面設置好數據庫服務器的地址、用戶名、密碼就可以備份還原數據了。需要注意的是:
·使用時候要在同級目錄下建一個Backup目錄,權限需要可寫,用於存放導出的腳本。
·當備份的數據庫比較大的時候,服務器腳本超時時間要調大一些。
·支持分卷備份,還原時候只要選擇分卷備份的第一個腳本就會自動還原所有的腳本。
·分卷文件大小不要太大,最好不超過2MB。
·安全起見,腳本不用時候記得從服務器上刪除。
打包文件下載