我时不时需要在测试服务器上加载生产服务器的实时数据,以前都是把数据下载到本地的测试服务器后,手工键入一些命令完成数据加载,每次都要花费几分钟时间。为了避免一再“浪费”这几分钟,我今天一次性投入了几小时完成了一个 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"; }
Leave a Reply