【问题标题】:How to duplicate a MySQL database on the same server如何在同一台服务器上复制 MySQL 数据库
【发布时间】:2011-07-11 14:59:09
【问题描述】:

我有一个大型 MySQL 数据库,我们称之为 live_db,我想在同一台机器上复制它以提供一个可以使用的测试系统 (test_db),包括表结构和数据。 我想定期用live_db 的内容更新test_db;如果可能,增量。

MySQL 中是否有一些内置机制可以做到这一点?我认为主从复制不是我想要的,因为应该可以更改test_db 中的数据。不过,这些更改不必保留。

问候,

CGD

【问题讨论】:

  • 目前为 12.9 GB,并且还在不断增长。
  • 万一你没有auto_increment 需要担心的密钥,你可以设置复制,但大部分时间保持从站停止。然后,您会定期START SLAVE;,它会自动更新。如果您有任何 auto_increment 字段,但这会导致键冲突。

标签: mysql database sync replicate


【解决方案1】:

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

请注意,由于您的数据很大,因此需要很长时间。

【讨论】:

  • 这是我找到的最好的方法。我期待听到更好的方法。
  • 正如我在上面的评论中所说,数据库目前为 12.9 GB 并且还在增长。进行完全转储需要花费大量时间。
  • 你真的需要 DROP|CREATE 数据库吗:mysqldump 无论如何都会包含 DROP TABLEs,所以除非你在测试数据库中生成表(因此除非手动删除,否则它仍然存在),你不需要需要前两行吗?
  • 请注意,如果您想恢复到不同的数据库,请不要使用左侧的 --database orig_db 参数。 mysqldump 将包含一个“使用orig_db;”在其输出中声明,您将不会写入您在管道右侧指定的数据库。学到了一个艰难的方式。 : - /
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    多年来,我一直在不同的环境中使用中小型数据库(1 G 到 100 G)进行此操作。快速而肮脏的mysqldump 适用于较小的数据集;它们越小越好。

    当您超过 5-10 GB 时,根据 MySQL 负载,quick and dirty 不再削减它。

    为什么mysqldump 可能还不够

    MySQLdump 的问题在于,当它转储时,活动数据库要么无法使用,要么使用起来非常尴尬,要么备份将不一致。除非您有足够宽的时间窗口,此时实时数据库的不可用并不重要,因为无论如何都不需要使用数据库(例如,深夜)。

    默认选项(here 讨论原因)使数据库在转储时几乎无法使用,除非使用只是读取数据而已。在一个繁忙的电子商务网站上,您正面临客户堆积如山的崩溃。

    所以你使用 InnoDB 和现代选项(据我所知,不是默认值)

    --single-transaction --skip-lock-tables
    

    这允许站点在转储期间运行,尽管比正常速度慢。根据使用情况,它可能会明显变慢。

    当您使用它时,还可以转储其他可能很重要的数据:

    --events --triggers --routines
    

    (...哦,这仍然不会转储用户权限。用作测试可能并不那么重要)。

    有一个我发现“建议”(!)作为“伟大的黑客”的解决方法,它基本上禁用事务完整性允许数据库在转储时全速运行。有点像从你的车上卸下刹车来减轻它的重量并让它跑得更快,是的,它会起作用,但它会有一些你可能不会立即注意到的副作用。你几乎肯定迟早会注意到它们 - 就像刹车一样,它会是你最需要它们的时候,而且它不会很漂亮。

    但是,对于 test 数据库,它仍然可以工作。

    Xtrabackup

    如果你有一个“严肃”的数据库,为什么没有a "serious" backup

    从属复制

    如果您有多余的空间(现在 20 Gb 并不多),另一种可能性是使用辅助数据库。

    您可以在不同端口上的同一服务器上安装第二个 MySQL 服务器副本,并让它成为从属服务器(服务器将在存储速度方面受到性能影响)。然后你将拥有两个相同的数据库(live master,live slave)。 第一次您仍然需要运行完整转储以使它们同步,其中涉及的所有问题。

    当您需要克隆测试数据库时,停止从属复制 - 活动从属现在将及时保持“冻结”状态 - 并使用 MySQLbackup 或仅复制数据文件将活动从属备份到测试数据库。完成后,重新启动复制。

    对活动主节点的影响可以忽略不计,从节点实际上可以用于非更新关键选择。

    【讨论】:

      【解决方案4】:

      如果您更喜欢 MySQL 迁移工具包,您可以在数据映射步骤中双击模式名称并更改目标模式名称。

      【讨论】:

        【解决方案5】:

        对于所有 mac 用户,使用 sequel pro,您只需转到数据库(菜单)-> 复制数据库。完成!

        【讨论】:

          【解决方案6】:

          这个解决方案很好用,但如果你使用 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;
          }
          

          补充说明:

          1. 我不得不在我的 SQL 语句周围使用双引号 (") 而不是单引号 (')。

          2. 我必须使用与号 (&) 来分隔我的不同命令。

          3. 此示例不包括对新数据库名称的验证(isDestinationDbNameValid() 方法)。无需提及您永远不应该相信用户输入...

          4. 您还必须编写自定义方法来验证数据库副本是否按预期工作(isDestinationDbExist() 方法)。 您至少应该验证数据库是否存在,您的设置中的表是否存在,并且可以选择验证存储的程序。

          我的朋友们,明智地使用武力,

          来自蒙特利尔的Jonathan Parent-Lévesque

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-06-07
            • 1970-01-01
            • 2016-09-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多