码疯窝

SAE 只允许用户通过phpMyAdmin管理远程数据库, 在做本地SAE环境开发的时候是是分不方便的.
因此写了点代码能够方便的将远程数据库同步到本地来.

一、SAE 无程数据库备份
可以通过SAE 提供的Cron, Storage, DeferredJob 来实现数据库备份. 其中有用到我的PhpLog 日志插件. 代码如下:

/cron/dbBackup.php

require('..' . DIRECTORY_SEPARATOR . 'ext' . DIRECTORY_SEPARATOR . 'PhpLog' . DIRECTORY_SEPARATOR . 'PhpLog.php');

$filename = SAE_MYSQL_DB . '-' . date('Ymd-his') . '-' . substr(time(), -4, 4) . '.sql';
$dj = new SaeDeferredJob();
$taskID = $dj->addTask("export", "mysql", "wordpress", "dbBackup/$filename", SAE_MYSQL_DB, "", "");
if ($taskID === false) {
	PhpLog::log('数据库备份失败 errno:' . $dj->errno() . ' errmsg:' . $dj->errmsg(),'error','dbBackup'); 
} else {
	PhpLog::log('数据库备份成功 id:' . $taskID, 'info', 'dbBackup'); 
}

然后需要在将此代码加入Cron 每月1号定时备份. 在config.yaml 加入以下代码:
config.yaml

name: dbBackup
version: 2
cron:
- description: db backup
  url: /cron/dbBackup.php
  schedule: "0 0 1 * *"

此时 dbBackup.php 是没有加入访问控制的. 所有人都可以通过web访问. 于是我们可以在dbBackup.php加入不允许通过web访问.

if(!isset($_SERVER['argv'])) {// || strncasecmp(php_sapi_name(),'cli',3))
	PhpLog::log('外部非法运行, 来自IP: ' . $_SERVER["REMOTE_ADDR"] . ' 运行环境: ' . $env, 'warning', 'dbBackup'); 
	echo "<script language='javascript'>location.href='//www.madcoder.cn/404.php';</script>";
	die();
}

到此处, 数据库备份代码已完成.

一、还原到本地数据库.
原理很简单, 就是通过Storage API 获取到最新的备份文件, 然后更新到本地数据库.
首先需要一个页面输出最新备份SQL文件.
/cron/dbRestore.php

$storage = new SaeStorage();
$list = $storage->getListByPath('wordpress', 'dbBackup');
if (count($list['files']) === 0) {
	echo '无文件';
	die;
}
header("Content-type: text/html; charset=utf-8");
echo $storage->read('wordpress', $list['files'][0]['fullName']);

其次需要一个本地的php脚本导入SQL文件到数据库.
/cron/dbRestore.local.php

define("DB_HOST", 'localhost');
define("DB_USER", 'root');
define("DB_PASSWORD", 'root');
define("DB_NAME", 'app_madcoder');
define("DOWNLOAD_LINK", '//www.madcoder.cn/cron/dbRestore.php');  // 查看最新备份文件的页面.

define("RESTORE_SQL", 'restore.sql'); // 导入成功后所要执行的SQL. 文件不存在刚不执行.

echo iconv('UTF-8','GBK','最新备份文件下载中...').PHP_EOL;

$sql = file_get_contents(DOWNLOAD_LINK);

echo iconv('UTF-8','GBK','文件下载成功, 正在写入文件').PHP_EOL;

$filename = md5(time()) . '.sql';

if ($fp = fopen($filename, "w")) {
	@fwrite($fp, $sql);
	if (@fwrite($fp, $sql)) {
		echo iconv('UTF-8','GBK','写入文件成功, 准备导入MYSQL').PHP_EOL;
		fclose($fp);
	} else {
		echo iconv('UTF-8','GBK','写文件失败.').PHP_EOL;
		fclose($fp);
		die;
	}
} 
echo iconv('UTF-8','GBK','正在还原数据库,请稍等....').PHP_EOL;
$rst = exec('mysql -h ' . DB_HOST . ' -u ' . DB_USER . ' -p' . DB_PASSWORD .' --default-character-set=utf8 ' . DB_NAME. ' < ' . $filename); 

unlink($filename); // SQL 导入后删除临时文件.

if (file_exists(RESTORE_SQL)) {
	echo iconv('UTF-8','GBK','正在执行还原SQL.').PHP_EOL;
	$mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
	$sql = str_replace(PHP_EOL, '', file_get_contents(RESTORE_SQL)); 
	$rst = $mysqli->multi_query($sql);
	$errors = $mysqli->error;
	if ($errors) {
		echo iconv('UTF-8','GBK','还原SQL执行失败, 错误: ' . $errors).PHP_EOL;
	} else {
		echo iconv('UTF-8','GBK','还原SQL执行成功.').PHP_EOL;
	}
	$mysqli->close();
}
echo iconv('UTF-8','GBK','程序结束.').PHP_EOL;

同时, 支持在导入完成后执行指定SQL文件. 因为我在远程库里site url 是 www.madcoder.cn, dump到本地是, 我需要它变成 1.madcoder.sae.com 之类的. 加入 restore.sql:

update wp_posts set guid = replace(guid, 'www.madcoder.cn', '1.madcoder.sae.com') 
where guid like '%www.madcoder.cn%';

update wp_options set option_value = replace(option_value, 'www.madcoder.cn', '1.madcoder.sae.com') 
where option_value like '%www.madcoder.cn%';

这样一键dump下来远程数据库之后本地就可以直接进行开发调试了.

当然, 这里的dbRestore.php是必须加入访问控制的, 不然所以人都能轻松dump下你的整个数据库那就麻烦了.
我们可以限定IP访问, 加上访问key, 等等方式来控制访问. 当然, 安全性很低, 大家自己取舍. 代码就不贴了, 不然人人能dump我的库了.

然后运行dbRestore.local.php 将本地数据库还原到最近远程备份.
运行结果如下图:

sae-db-backup-restore

继续查看有关 技术随笔的文章