A tool to synchronise Magento database between servers

我时不时需要在测试服务器上加载生产服务器的实时数据,以前都是把数据下载到本地的测试服务器后,手工键入一些命令完成数据加载,每次都要花费几分钟时间。为了避免一再“浪费”这几分钟,我今天一次性投入了几小时完成了一个 php 脚本。虽然这是为 magento 的数据迁移而写的脚本,但我写完一看,用在其他地方也是可以的。

为了安全起见,该脚本是用 php 命令行运行的,所有输出针对 terminal 美化,不是 browser。保存源码为 data_mover.php,同一目录下要有 mysqldump 得到的经 gzip 的 sql 文件,文件名以 FILENAME_PREFIX 开头,以 .sql.gz 结尾。启动时只需键入

/path/to/php -f data_mover.php

即可。

初始化 PDO 对象时,按理只需要 host=localhost,不需要 unix_socket=MYSQL_SOCKET。但奇怪的是,如果通过 apache 调用本程序(虽然不是本程序的初衷,但我希望它在浏览器下也能运行),仅指定 host=localhost 作 PDO __construct() 参数,会产生一个莫名其妙的错误:

SQLSTATE[HY000] [2002] Can’t connect to local MySQL server through socket ‘/tmp/mysql.sock’ (2)

似乎是 PDO bug。可以通过 host=127.0.0.1 或者追加 unix_socket=MYSQL_SOCKET 来避免。如果通过 php 命令行启动则没有这个问题。

而后,还涉及怎么删除所有数据表的问题。看似简单的一个问题,我发现 mysql 竟然没有一个类似于 DROP/TRUNCATE TABLE * 单行命令。于是除了本脚本用的方法外,我还想了不下两种办法:

其一,删掉整个数据库重新创建。
mysqladmin -f -h localhost -u (DB_ROOT_USERNAME) -p(DB_ROOT_PASSWORD) drop (DB_NAME)
mysqladmin -h localhost -u (DB_ROOT_USERNAME) -p(DB_ROOT_PASSWORD) create (DB_NAME)
但这需要比数据表操作更高权限的用户,在这个无关大局的脚本里去使用高权限的用户的密码,实在非我所愿。

其二,是我 google 来的,方法很巧,但很可惜,因为 foreign keys 的存在,运行这条命令会出错。
mysqldump -u (DB_USERNAME) -p(DB_PASSWORD) –add-drop-table –no-data (DB_NAME) | grep ^DROP | mysql -u (DB_USERNAME) -p(DB_PASSWORD) (DB_NAME)


<?php

define('MYSQL_SOCKET', '/path/to/mysql/socket');
define('DB_NAME', 'db_name');
define('DB_USERNAME', 'db_username');
define('DB_PASSWORD', 'db_password');
define('FILENAME_PREFIX', 'filename_prefix');
define('TEST_URL', 'http://test.domain/');

if ($handle = opendir(dirname(__FILE__))) {
$found = false;
/* This is the correct way to loop over the directory. */
while (false !== ($file = readdir($handle))) {
if (substr($file, 0, 12) == FILENAME_PREFIX && substr($file, -7) == '.sql.gz') {
if ($found) {
//compare which one is newer
if (filemtime($file) > filemtime($fileFound)) {
$fileFound = $file;
}
}
else {
$found = true;
$fileFound = $file;
}
}
}

if ($found) {
echo "Found the newest file $fileFound, and will work on it.\n";
try {
$pdo = new PDO(
'mysql:host=localhost;dbname=' . DB_NAME . ';unix_socket=' . MYSQL_SOCKET,
DB_USERNAME,
DB_PASSWORD,
array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8")
);
echo "Connected to database.\n";
}
catch (Exception $e) {
die ("Error occurred when connecting to database. Quoting error message: " . $e->getMessage() . "\n");
}
$sql = 'SET FOREIGN_KEY_CHECKS = 0';
$pdo->prepare($sql)->execute();

/* query all tables */
$sql = "SHOW TABLES FROM ". DB_NAME;
$stmt = $pdo->prepare($sql);
$stmt->execute();
$result = $stmtDale->fetchAll(PDO::FETCH_COLUMN, 0);
$magentoTableNames = array();
/* add table name to array */
foreach ($result as $tableName) {
if (substr($tableName, 0, 8) == 'magento_') {
$magentoTableNames[] = $tableName;
}
}

//drop all magento tables
$count = count($magentoTableNames);
if ($count > 0) {
$sql = 'DROP TABLE '. implode(',', $magentoTableNames);
$pdo->prepare($sql)->execute();
echo "Found and dropped $count magento tables.\n";
}
else {
echo "No existing magento tables found.\n";
}

//import data via pipe
echo "Importing data. It may take a while...\n";
$output = shell_exec("gunzip < $fileFound | mysql -h localhost -u " . DB_USERNAME . " -p" . DB_PASSWORD . " " . DB_NAME);
echo "Importing data completed.\n";
if ($output) {
echo "This is output during data import:\n$output\n";
}

//after import, change some data for test domains
$sql = 'UPDATE magento_core_config_data SET value=? WHERE scope=? AND scope_id=? AND path=?';
$stmt = $pdo->prepare($sql);
$stmt->execute(array(TEST_URL, 'default', 0, 'web/unsecure/base_url'));
$stmt->execute(array(TEST_URL, 'default', 0, 'web/secure/base_url'));
$stmt->execute(array(TEST_URL, 'websites', 2, 'web/unsecure/base_url'));
$stmt->execute(array(TEST_URL, 'websites', 2, 'web/secure/base_url'));
echo "Config data changed to fit testing environment.\n";
echo "All missions completed.\n";
}
else {
echo "File not found. What else can I do?\n";
}
closedir($handle);
}
else {
echo "File access not permitted.\n";
}

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *