我有一个大型 MySQL 数据库,我们称之为live_db
,我想在同一台机器上复制它以提供一个测试系统来玩 ( test_db
),包括表结构和数据。我想定期更新;test_db
的内容 live_db
如果可能,增量。
MySQL中是否有一些内置机制可以做到这一点?我认为主从复制不是我想要的,因为应该可以更改test_db
. 但是,不必保留这些更改。
问候,
CGD
命令行客户端将mysql
接受来自标准输入的 SQL 语句流。因此,您可以通过管道将 的输出mysqldump
直接输入到mysql
命令行中。作为一项 cron 作业执行此操作将定期用更新的实时数据覆盖您的测试数据:
mysql --user=username --password=passwd -e 'DROP DATABASE test_db;'
mysql --user=username --password=passwd -e 'CREATE DATABASE test_db;'
mysqldump --user=username --password=passwd live_db | mysql --user=username --password=passwd test_db
请注意,由于您的数据很大,因此需要很长时间。
Michaels answer abowe 效果很好,但不会复制事件、存储过程或触发器。
要复制这些更多的开关,mysqldump 需要:
--events --triggers --routines
要补充已制作的副本:
mysqldump --user=username --password=passwd --no-data --no-create-info --no-create-db --events --triggers --routines live_db | mysql --user=username --password=passwd test_db
多年来,我一直在不同的环境中这样做,使用中小型数据库(1 G 到 100 G)。快速而肮脏的mysqldump
作品适用于较小的数据集;它们越小,效果越好。
当您超过 5-10 GB 时,取决于 MySQL 负载,quick 和dirty不再削减它。
mysqldump
可能还不够MySQLdump 的问题在于,当它转储时,实时数据库要么无法使用,要么使用起来非常尴尬,要么备份将不一致。除非您有足够宽的时间窗口,此时实时数据库的不可用性并不重要,因为无论如何都不需要使用数据库(例如,深夜)。
默认选项(此处讨论原因)使数据库在转储时几乎不可用,除非使用只是读取数据而很少。在一个繁忙的电子商务网站上,您正在查看客户堆积崩溃。
所以你使用 InnoDB 和现代选项(据我所知,不是默认值)
--single-transaction --skip-lock-tables
这允许站点在转储期间运行,尽管比正常速度慢。根据使用情况,它可能会明显变慢。
当您使用它时,还可以转储其他可能很重要的数据:
--events --triggers --routines
(......哦,这仍然不会转储用户权限。用作测试也许它不是那么重要)。
有一个我发现“建议”(!)作为“伟大的黑客”的解决方法,它基本上禁用事务完整性,允许数据库在转储时全速运行。有点像从汽车上卸下刹车以减轻重量并使其运行得更快,是的,它会起作用,但它会产生一些您可能不会立即注意到的副作用。你几乎肯定迟早会注意到它们——就像刹车一样,这将是你最需要它们的时候,而且它不会很漂亮。
但是,对于测试数据库,它仍然可以工作。
如果您有一个“严重”的数据库,那么没有“严重”备份的原因是什么?
如果您有多余的空间(现在,20 Gb 已经不多),另一种可能性是使用辅助数据库。
您可以在不同端口上的同一台服务器上安装第二个 MySQL 服务器副本,并让它成为从属服务器(服务器将受到性能影响,存储速度方面)。然后你将拥有两个相同的数据库(live master,live slave)。第一次您仍然需要运行完整转储以使它们同步,其中涉及的所有问题。
当您需要克隆测试数据库时,停止从属复制 - 活动从属现在将及时“冻结” - 并使用 MySQLbackup 或仅复制数据文件将活动从属备份到测试数据库。完成后,重新启动复制。
对活动主节点的影响可以忽略不计,从节点实际上可以用于非更新关键选择。
如果您更喜欢 MySQL 迁移工具包,您可以在数据映射步骤中双击模式名称并更改目标模式名称。
对于所有 mac 用户,使用 sequel pro,您只需转到数据库(菜单)-> 复制数据库。完毕!
此解决方案运行良好,但如果您使用 PHPunit 进行单元测试,它就无法解决问题。
在命令行中使用密码会生成一个警告,该警告会被 PHPUnit 捕获并生成异常(是的,这很重要……)
解决此问题的方法是使用配置文件。
就我而言,我不想在配置文件和 PHP 代码中都维护密码和用户,所以我从代码生成配置文件并检查它是否存在(否则我直接在命令行中使用用户名和密码作为后备选项)。
这是一个示例,在 PHP 中,如何复制设置数据库以创建具有不同名称的新数据库(例如,如果您要为每个客户管理具有不同子域/数据库的主域):
/**
* If the $dbName doesn't exist, then create it.
*
* @param $errorMessage String to return the error message.
* @param $dbName String name of the database to create.
* @param $cleanExisting Boolean if the database exist, clear it to recreate it.
*
* @return boolean ($dbExists)
*/
private function createDatabase(&$errorMessage, $dbName, $clearExisting = false){
$command = "";
$configurationString = "[client]" . "\r\n" . "user=" . parent::$support_user . "\r\n" . "password=" . md5(parent::$support_pass);
$dbExist = false;
$path = realpath(dirname(__FILE__));
$connectionString = " --defaults-extra-file=" . $path . "\mysql.cnf ";
$dbName = strtolower($dbName);
if ($this->isDestinationDbNameValid($errorMessage, $dbName)) {
$dbExist = $this->isDestinationDbExist($errorMessage, $dbName);
if (empty($errorMessage) and ($dbExist === false or $clearExisting === true)) {
if (file_put_contents($path . '/mysql.cnf', $configurationString) === false) {
$connectionString = " --user=" . parent::$support_user . " --password=" . md5(parent::$support_pass). " ";
}
if ($dbExist and $clearExisting) {
$command = $path . '/../../../mysql/bin/mysql ' . $connectionString . ' --execute="DROP DATABASE ' . $dbName .';" &';
}
$command .= '"' . $path . '/../../../mysql/bin/mysql" ' . $connectionString . ' --execute="CREATE DATABASE ' . $dbName . ';" &"' .
$path . '/../../../mysql/bin/mysqldump" ' . $connectionString . ' --events --triggers --routines setup | "' .
$path . '/../../../mysql/bin/mysql" ' . $connectionString . $dbName;
exec($command);
$dbExist = $this->isDestinationDbExist($errorMessage, $dbName);
if (!$dbExist) {
$errorMessage = parent::getErrorMessage("COPY_SETUP_DB_ERR", "An error occurred during the duplication process of the setup database.");
}
}
}
return $dbExist;
}
附加说明:
我不得不在我的 SQL 语句周围使用双引号 (") 而不是单引号 (')。
我不得不使用与号 (&) 来分隔我的不同命令。
此示例不包括对新数据库名称的验证(isDestinationDbNameValid() 方法)。无需提及您永远不应该相信用户输入......
您还必须编写自定义方法来验证数据库副本是否按预期工作(isDestinationDbExist() 方法)。您至少应该验证数据库是否存在,设置中的表是否存在,并且可以选择验证存储的程序。
我的朋友们,明智地使用武力,
来自蒙特利尔的 Jonathan Parent-Lévesque