218

我想编写一个脚本,将我当前的数据库复制sitedb1sitedb2同一个 mysql 数据库实例上。我知道我可以将 sitedb1 转储到 sql 脚本:

mysqldump -u root -p sitedb1 >~/db_name.sql

然后将其导入到sitedb2. 有没有更简单的方法,无需将第一个数据库转储到 sql 文件?

4

16 回答 16

420

正如手册在复制数据库中所说,您可以将转储直接通过管道传输到 mysql 客户端:

mysqldump db_name | mysql new_db_name

如果您使用的是 MyISAM,您可以复制文件,但我不建议这样做。这有点狡猾。

综合各种好的其他答案

mysqldump和命令都mysql接受用于设置连接详细信息(以及更多)的选项,例如:

mysqldump -u <user name> --password=<pwd> <original db> | mysql -u <user name> -p <new db>

此外,如果新数据库尚不存在,您必须事先创建它(例如使用echo "create database new_db_name" | mysql -u <dbuser> -p)。

于 2009-03-23T21:26:57.317 回答
88

使用 MySQL 实用程序

MySQL Utilities 包含一个不错的工具mysqldbcopy,它默认复制一个数据库,包括所有相关对象(“表、视图、触发器、事件、过程、函数和数据库级授权”)和数据从一个数据库服务器到相同或另一个数据库服务器。有很多选项可用于自定义实际复制的内容。

因此,要回答 OP 的问题:

mysqldbcopy \
    --source=root:your_password@localhost \
    --destination=root:your_password@localhost \
    sitedb1:sitedb2
于 2014-07-30T13:22:20.483 回答
29

最好和最简单的方法是在终端中输入这些命令并将权限设置为 root 用户。为我工作..!

:~$> mysqldump -u root -p db1 > dump.sql
:~$> mysqladmin -u root -p create db2
:~$> mysql -u root -p db2 < dump.sql
于 2017-05-29T06:24:09.747 回答
18
mysqladmin create DB_name -u DB_user --password=DB_pass && \
        mysqldump -u DB_user --password=DB_pass DB_name | \
        mysql     -u DB_user --password=DB_pass -h DB_host DB_name
于 2012-07-15T09:22:17.023 回答
13

您可以使用(在伪代码中):

FOREACH tbl IN db_a:
    CREATE TABLE db_b.tbl LIKE db_a.tbl;
    INSERT INTO db_b.tbl SELECT * FROM db_a.tbl;

我不使用 CREATE TABLE ... SELECT ... 语法的原因是为了保留索引。当然,这只会复制表格。视图和过程不会被复制,尽管可以以相同的方式完成。

请参阅创建表

于 2009-03-23T21:28:33.850 回答
13

您需要从终端/命令提示符运行命令。

mysqldump -u <user name> -p <pwd> <original db> | mysql -u <user name> <pwd> <new db>

例如:mysqldump -u root test_db1 | mysql -u root test_db2

这会将 test_db1 复制到 test_db2 并授予对 'root'@'localhost' 的访问权限

于 2015-04-21T06:49:29.510 回答
6

如果您安装了一个简单的方法phpmyadmin

转到您的数据库,选择“操作”选项卡,您可以看到“将数据库复制到”块。使用它,您可以复制数据库。

于 2018-02-13T09:43:46.240 回答
5

首先创建重复数据库:

CREATE DATABASE duplicateddb;

确保权限等都到位,并且:

mysqldump -u admin -p originaldb | mysql -u backup -p password duplicateddb;
于 2013-05-01T11:34:00.943 回答
4

正如Greg 的回答中提到的,mysqldump db_name | mysql new_db_name是在数据库之间传输数据的免费、安全且简单的方法。但是,它也确实很慢

如果您要备份数据,不能丢失数据(在此或其他数据库中),或者正在使用 以外的表innodb,那么您应该使用mysqldump.

如果您正在寻找用于开发的东西,将所有数据库备份到其他地方,并且mysql在一切都出错时可以轻松地清除和重新安装(可能手动),那么我可能会为您提供解决方案。

我找不到一个好的替代方案,所以我构建了一个脚本来自己做。我花了很多时间第一次让它工作,老实说,现在对它进行更改让我有点害怕。Innodb 数据库不应该像这样复制和粘贴。微小的变化会导致它以惊人的方式失败。自从我完成代码以来,我没有遇到过问题,但这并不意味着你不会。

系统测试(但仍可能失败):

  • Ubuntu 16.04,默认 mysql,innodb,每个表单独的文件
  • Ubuntu 18.04,默认 mysql,innodb,每个表单独的文件

我们已经切换到 docker 和整个 mysql 数据文件夹的简单副本,因此不再维护此脚本。留下它以防将来它能够帮助任何人。

它能做什么

  1. 获取sudo权限并验证您有足够的存储空间来克隆数据库
  2. 获取 root mysql 权限
  3. 创建一个以当前 git 分支命名的新数据库
  4. 将结构克隆到新数据库
  5. 切换到 innodb 的恢复模式
  6. 删除新数据库中的默认数据
  7. 停止 mysql
  8. 将数据克隆到新数据库
  9. 启动mysql
  10. 在新数据库中链接导入的数据
  11. 退出 innodb 的恢复模式
  12. 重启mysql
  13. 授予 mysql 用户访问数据库的权限
  14. 清理临时文件

与它相比如何mysqldump

在 3gb 数据库上,在我的机器上使用mysqldumpandmysql需要 40-50 分钟。使用这种方法,同样的过程只需要大约 8 分钟。

我们如何使用它

我们将 SQL 更改与代码一起保存,并且升级过程在生产和开发中都是自动化的,每组更改都会备份数据库以在出现错误时进行恢复。我们遇到的一个问题是,当我们处理一个需要更改数据库的长期项目时,必须在其中切换分支以修复一个或三个错误。

过去,我们为所有分支使用单个数据库,并且每当我们切换到与新数据库更改不兼容的分支时都必须重建数据库。当我们切换回来时,我们必须再次运行升级。

我们尝试mysqldump为不同的分支复制数据库,但是等待时间太长(40-50 分钟),在此期间我们无能为力。

该解决方案将数据库克隆时间缩短到了 1/5 时间(想想喝咖啡和洗手间的时间,而不是长时间的午餐)。

常见任务及其时间

在单个数据库上切换具有不兼容数据库更改的分支需要 50 多分钟,但在使用mysqldump此代码的初始设置时间之后根本没有时间。这段代码恰好比mysqldump.

以下是一些常见任务以及每种方法大概需要多长时间:

使用数据库更改创建功能分支并立即合并:

  • 单个数据库:~5 分钟
  • 克隆mysqldump时间:50-60 分钟
  • 使用此代码克隆:~18 分钟

使用数据库更改创建功能分支,切换到main错误修复,在功能分支上进行编辑,然后合并:

  • 单个数据库:~60 分钟
  • 克隆mysqldump时间:50-60 分钟
  • 使用此代码克隆:~18 分钟

使用数据库更改创建功能分支,切换到main错误修复 5 次,同时在其间对功能分支进行编辑,然后合并:

  • 单个数据库:~4 小时 40 分钟
  • 克隆mysqldump时间:50-60 分钟
  • 使用此代码克隆:~18 分钟

编码

除非您已阅读并理解上述所有内容,否则请勿使用它。它不再维护,因此随着时间的推移,它越来越有可能被破坏。

#!/bin/bash
set -e

# This script taken from: https://stackoverflow.com/a/57528198/526741

function now {
    date "+%H:%M:%S";
}

# Leading space sets messages off from step progress.
echosuccess () {
    printf "\e[0;32m %s: %s\e[0m\n" "$(now)" "$1"
    sleep .1
}
echowarn () {
    printf "\e[0;33m %s: %s\e[0m\n" "$(now)" "$1"
    sleep .1
}
echoerror () {
    printf "\e[0;31m %s: %s\e[0m\n" "$(now)" "$1"
    sleep .1
}
echonotice () {
    printf "\e[0;94m %s: %s\e[0m\n" "$(now)" "$1"
    sleep .1
}
echoinstructions () {
    printf "\e[0;104m %s: %s\e[0m\n" "$(now)" "$1"
    sleep .1
}
echostep () {
    printf "\e[0;90mStep %s of 13:\e[0m\n" "$1"
    sleep .1
}

MYSQL_CNF_PATH='/etc/mysql/mysql.conf.d/recovery.cnf'
OLD_DB='YOUR_DATABASE_NAME'
USER='YOUR_MYSQL_USER'

# You can change NEW_DB to whatever you like
# Right now, it will append the current git branch name to the existing database name
BRANCH=`git rev-parse --abbrev-ref HEAD`
NEW_DB="${OLD_DB}__$BRANCH"

THIS_DIR=./site/upgrades
DB_CREATED=false

tmp_file () {
    printf "$THIS_DIR/$NEW_DB.%s" "$1"
}
sql_on_new_db () {
    mysql $NEW_DB --unbuffered --skip-column-names -u root -p$PASS 2>> $(tmp_file 'errors.log')
}

general_cleanup () {
    echoinstructions 'Leave this running while things are cleaned up...'

    if [ -f $(tmp_file 'errors.log') ]; then
        echowarn 'Additional warnings and errors:'
        cat $(tmp_file 'errors.log')
    fi

    for f in $THIS_DIR/$NEW_DB.*; do
        echonotice 'Deleting temporary files created for transfer...'
        rm -f $THIS_DIR/$NEW_DB.*
        break
    done

    echonotice 'Done!'
    echoinstructions "You can close this now :)"
}

error_cleanup () {
    exitcode=$?

    # Just in case script was exited while in a prompt
    echo

    if [ "$exitcode" == "0" ]; then
        echoerror "Script exited prematurely, but exit code was '0'."
    fi

    echoerror "The following command on line ${BASH_LINENO[0]} exited with code $exitcode:"
    echo "             $BASH_COMMAND"

    if [ "$DB_CREATED" = true ]; then
        echo
        echonotice "Dropping database \`$NEW_DB\` if created..."
        echo "DROP DATABASE \`$NEW_DB\`;" | sql_on_new_db || echoerror "Could not drop database \`$NEW_DB\` (see warnings)"
    fi

    general_cleanup

    exit $exitcode
}

trap error_cleanup EXIT

mysql_path () {
    printf "/var/lib/mysql/"
}
old_db_path () {
    printf "%s%s/" "$(mysql_path)" "$OLD_DB"
}
new_db_path () {
    printf "%s%s/" "$(mysql_path)" "$NEW_DB"
}
get_tables () {
    (sudo find /var/lib/mysql/$OLD_DB -name "*.frm" -printf "%f\n") | cut -d'.' -f1 | sort
}

STEP=0


authenticate () {
    printf "\e[0;104m"
    sudo ls &> /dev/null
    printf "\e[0m"
    echonotice 'Authenticated.'
}
echostep $((++STEP))
authenticate

TABLE_COUNT=`get_tables | wc -l`
SPACE_AVAIL=`df -k --output=avail $(mysql_path) | tail -n1`
SPACE_NEEDED=(`sudo du -s $(old_db_path)`)
SPACE_ERR=`echo "$SPACE_AVAIL-$SPACE_NEEDED" | bc`
SPACE_WARN=`echo "$SPACE_AVAIL-$SPACE_NEEDED*3" | bc`
if [ $SPACE_ERR -lt 0 ]; then
    echoerror 'There is not enough space to branch the database.'
    echoerror 'Please free up some space and run this command again.'
    SPACE_AVAIL_FORMATTED=`printf "%'d" $SPACE_AVAIL`
    SPACE_NEEDED_FORMATTED=`printf "%'${#SPACE_AVAIL_FORMATTED}d" $SPACE_NEEDED`
    echonotice "$SPACE_NEEDED_FORMATTED bytes needed to create database branch"
    echonotice "$SPACE_AVAIL_FORMATTED bytes currently free"
    exit 1
elif [ $SPACE_WARN -lt 0 ]; then
    echowarn 'This action will use more than 1/3 of your available space.'
    SPACE_AVAIL_FORMATTED=`printf "%'d" $SPACE_AVAIL`
    SPACE_NEEDED_FORMATTED=`printf "%'${#SPACE_AVAIL_FORMATTED}d" $SPACE_NEEDED`
    echonotice "$SPACE_NEEDED_FORMATTED bytes needed to create database branch"
    echonotice "$SPACE_AVAIL_FORMATTED bytes currently free"
    printf "\e[0;104m"
    read -p " $(now): Do you still want to branch the database? [y/n] " -n 1 -r CONFIRM
    printf "\e[0m"
    echo
    if [[ ! $CONFIRM =~ ^[Yy]$ ]]; then
        echonotice 'Database was NOT branched'
        exit 1
    fi
fi

PASS='badpass'
connect_to_db () {
    printf "\e[0;104m %s: MySQL root password: \e[0m" "$(now)"
    read -s PASS
    PASS=${PASS:-badpass}
    echo
    echonotice "Connecting to MySQL..."
}
create_db () {
    echonotice 'Creating empty database...'
    echo "CREATE DATABASE \`$NEW_DB\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci" | mysql -u root -p$PASS 2>> $(tmp_file 'errors.log')
    DB_CREATED=true
}
build_tables () {
    echonotice 'Retrieving and building database structure...'
    mysqldump $OLD_DB --skip-comments -d -u root -p$PASS 2>> $(tmp_file 'errors.log') | pv --width 80  --name " $(now)" > $(tmp_file 'dump.sql')
    pv --width 80  --name " $(now)" $(tmp_file 'dump.sql') | sql_on_new_db
}
set_debug_1 () {
    echonotice 'Switching into recovery mode for innodb...'
    printf '[mysqld]\ninnodb_file_per_table = 1\ninnodb_force_recovery = 1\n' | sudo tee $MYSQL_CNF_PATH > /dev/null
}
set_debug_0 () {
    echonotice 'Switching out of recovery mode for innodb...'
    sudo rm -f $MYSQL_CNF_PATH
}
discard_tablespace () {
    echonotice 'Unlinking default data...'
    (
        echo "USE \`$NEW_DB\`;"
        echo "SET foreign_key_checks = 0;"
        get_tables | while read -r line;
            do echo "ALTER TABLE \`$line\` DISCARD TABLESPACE; SELECT 'Table \`$line\` imported.';";
        done
        echo "SET foreign_key_checks = 1;"
    ) > $(tmp_file 'discard_tablespace.sql')
    cat $(tmp_file 'discard_tablespace.sql') | sql_on_new_db | pv --width 80 --line-mode --size $TABLE_COUNT --name " $(now)" > /dev/null
}
import_tablespace () {
    echonotice 'Linking imported data...'
    (
        echo "USE \`$NEW_DB\`;"
        echo "SET foreign_key_checks = 0;"
        get_tables | while read -r line;
            do echo "ALTER TABLE \`$line\` IMPORT TABLESPACE; SELECT 'Table \`$line\` imported.';";
        done
        echo "SET foreign_key_checks = 1;"
    ) > $(tmp_file 'import_tablespace.sql')
    cat $(tmp_file 'import_tablespace.sql') | sql_on_new_db | pv --width 80 --line-mode --size $TABLE_COUNT --name " $(now)" > /dev/null
}
stop_mysql () {
    echonotice 'Stopping MySQL...'
    sudo /etc/init.d/mysql stop >> $(tmp_file 'log')
}
start_mysql () {
    echonotice 'Starting MySQL...'
    sudo /etc/init.d/mysql start >> $(tmp_file 'log')
}
restart_mysql () {
    echonotice 'Restarting MySQL...'
    sudo /etc/init.d/mysql restart >> $(tmp_file 'log')
}
copy_data () {
    echonotice 'Copying data...'
    sudo rm -f $(new_db_path)*.ibd
    sudo rsync -ah --info=progress2 $(old_db_path) --include '*.ibd' --exclude '*' $(new_db_path)
}
give_access () {
    echonotice "Giving MySQL user \`$USER\` access to database \`$NEW_DB\`"
    echo "GRANT ALL PRIVILEGES ON \`$NEW_DB\`.* to $USER@localhost" | sql_on_new_db
}

echostep $((++STEP))
connect_to_db

EXISTING_TABLE=`echo "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '$NEW_DB'" | mysql --skip-column-names -u root -p$PASS 2>> $(tmp_file 'errors.log')`
if [ "$EXISTING_TABLE" == "$NEW_DB" ]
    then
        echoerror "Database \`$NEW_DB\` already exists"
        exit 1
fi

echoinstructions "The hamsters are working. Check back in 5-10 minutes."
sleep 5

echostep $((++STEP))
create_db
echostep $((++STEP))
build_tables
echostep $((++STEP))
set_debug_1
echostep $((++STEP))
discard_tablespace
echostep $((++STEP))
stop_mysql
echostep $((++STEP))
copy_data
echostep $((++STEP))
start_mysql
echostep $((++STEP))
import_tablespace
echostep $((++STEP))
set_debug_0
echostep $((++STEP))
restart_mysql
echostep $((++STEP))
give_access

echo
echosuccess "Database \`$NEW_DB\` is ready to use."
echo

trap general_cleanup EXIT

如果一切顺利,您应该会看到如下内容:

示例数据库的脚本输出屏幕截图

于 2019-08-16T16:33:52.677 回答
2

您可以执行以下操作:

mysqldump -u[username] -p[password] database_name_for_clone 
 | mysql -u[username] -p[password] new_database_name
于 2015-07-21T06:18:39.253 回答
1

此语句在 MySQL 5.1.7 中添加,但被发现有危险并在 MySQL 5.1.23 中删除。它旨在使升级 5.1 之前的数据库能够使用 5.1 中实现的编码来将数据库名称映射到数据库目录名称。但是,使用此语句可能会导致数据库内容丢失,这就是它被删除的原因。不要在它存在的早期版本中使用 RENAME DATABASE。

要使用新编码执行升级数据库名称的任务,请改用 ALTER DATABASE db_name UPGRADE DATA DIRECTORY NAME:http: //dev.mysql.com/doc/refman/5.1/en/alter-database.html

于 2013-05-16T11:10:11.157 回答
1

使用 Mydumper

sudo apt install mydumper

生成转储

mydumper --user=YOUR_USER --password=YOUR_PASSWORD -B YOUR_CURRENT_DB \ 
         --triggers --routines --events --outputdir YOUR_OUTPUT_DIR

负载转储

myloader --user=YOUR_USER --password=YOUR_PASSWORD --database=YOUR_NEW_DB \ 
         --directory=YOUR_OUTPUT_DIR
于 2021-05-06T06:55:18.020 回答
0

除了Greg 的 answernew_db_name之外,如果尚不存在 ,这是最简单和最快的方法:

echo "create database new_db_name" | mysql -u <user> -p <pwd> 
mysqldump -u <user> -p <pwd> db_name | mysql -u <user> -p <pwd> new_db_name
于 2015-07-06T10:01:39.733 回答
0

如果您的原始数据库中有触发器,则可以通过在导入之前通过管道替换来避免“触发器已存在”错误:

mysqldump -u olddbuser -p -d olddbname | sed "s/`olddbname`./`newdbname`./" | mysql -u newdbuser -p -D newdbname
于 2019-03-04T20:10:53.033 回答
0

使用 MySQL Workbench,您可以使用 Database > Migration Wizard 将数据库复制到相同或其他服务器实例。我相信它可以在服务器端工作,因此它应该是复制大型数据库的一个很好的解决方案。

于 2021-11-04T10:14:54.780 回答
0

你可以做:

CREATE DATABASE copy_of_db;
create table copy_of_db.table LIKE source_db.table;

If you want to copy data too:
INSERT INTO copy_of_db.table SELECT * FROM source_db.table;

对所有表、函数、过程等重复

(mysqldump 是正确的方法,但这是一种在许多情况下有用的快速而肮脏的解决方案)

于 2021-11-26T09:53:48.763 回答