我想编写一个脚本,将我当前的数据库复制sitedb1
到sitedb2
同一个 mysql 数据库实例上。我知道我可以将 sitedb1 转储到 sql 脚本:
mysqldump -u root -p sitedb1 >~/db_name.sql
然后将其导入到sitedb2
. 有没有更简单的方法,无需将第一个数据库转储到 sql 文件?
正如手册在复制数据库中所说,您可以将转储直接通过管道传输到 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
)。
MySQL Utilities 包含一个不错的工具mysqldbcopy
,它默认复制一个数据库,包括所有相关对象(“表、视图、触发器、事件、过程、函数和数据库级授权”)和数据从一个数据库服务器到相同或另一个数据库服务器。有很多选项可用于自定义实际复制的内容。
因此,要回答 OP 的问题:
mysqldbcopy \
--source=root:your_password@localhost \
--destination=root:your_password@localhost \
sitedb1:sitedb2
最好和最简单的方法是在终端中输入这些命令并将权限设置为 root 用户。为我工作..!
:~$> mysqldump -u root -p db1 > dump.sql
:~$> mysqladmin -u root -p create db2
:~$> mysql -u root -p db2 < dump.sql
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
您可以使用(在伪代码中):
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 ... 语法的原因是为了保留索引。当然,这只会复制表格。视图和过程不会被复制,尽管可以以相同的方式完成。
请参阅创建表。
您需要从终端/命令提示符运行命令。
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' 的访问权限
如果您安装了一个简单的方法phpmyadmin
:
转到您的数据库,选择“操作”选项卡,您可以看到“将数据库复制到”块。使用它,您可以复制数据库。
首先创建重复数据库:
CREATE DATABASE duplicateddb;
确保权限等都到位,并且:
mysqldump -u admin -p originaldb | mysql -u backup -p password duplicateddb;
正如Greg 的回答中提到的,mysqldump db_name | mysql new_db_name
是在数据库之间传输数据的免费、安全且简单的方法。但是,它也确实很慢。
如果您要备份数据,不能丢失数据(在此或其他数据库中),或者正在使用 以外的表innodb
,那么您应该使用mysqldump
.
如果您正在寻找用于开发的东西,将所有数据库备份到其他地方,并且mysql
在一切都出错时可以轻松地清除和重新安装(可能手动),那么我可能会为您提供解决方案。
我找不到一个好的替代方案,所以我构建了一个脚本来自己做。我花了很多时间第一次让它工作,老实说,现在对它进行更改让我有点害怕。Innodb 数据库不应该像这样复制和粘贴。微小的变化会导致它以惊人的方式失败。自从我完成代码以来,我没有遇到过问题,但这并不意味着你不会。
系统测试(但仍可能失败):
我们已经切换到 docker 和整个 mysql 数据文件夹的简单副本,因此不再维护此脚本。留下它以防将来它能够帮助任何人。
sudo
权限并验证您有足够的存储空间来克隆数据库mysqldump
在 3gb 数据库上,在我的机器上使用mysqldump
andmysql
需要 40-50 分钟。使用这种方法,同样的过程只需要大约 8 分钟。
我们将 SQL 更改与代码一起保存,并且升级过程在生产和开发中都是自动化的,每组更改都会备份数据库以在出现错误时进行恢复。我们遇到的一个问题是,当我们处理一个需要更改数据库的长期项目时,必须在其中切换分支以修复一个或三个错误。
过去,我们为所有分支使用单个数据库,并且每当我们切换到与新数据库更改不兼容的分支时都必须重建数据库。当我们切换回来时,我们必须再次运行升级。
我们尝试mysqldump
为不同的分支复制数据库,但是等待时间太长(40-50 分钟),在此期间我们无能为力。
该解决方案将数据库克隆时间缩短到了 1/5 时间(想想喝咖啡和洗手间的时间,而不是长时间的午餐)。
在单个数据库上切换具有不兼容数据库更改的分支需要 50 多分钟,但在使用mysqldump
此代码的初始设置时间之后根本没有时间。这段代码恰好比mysqldump
.
以下是一些常见任务以及每种方法大概需要多长时间:
mysqldump
时间:50-60 分钟main
错误修复,在功能分支上进行编辑,然后合并:mysqldump
时间:50-60 分钟main
错误修复 5 次,同时在其间对功能分支进行编辑,然后合并:mysqldump
时间:50-60 分钟除非您已阅读并理解上述所有内容,否则请勿使用它。它不再维护,因此随着时间的推移,它越来越有可能被破坏。
#!/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
如果一切顺利,您应该会看到如下内容:
您可以执行以下操作:
mysqldump -u[username] -p[password] database_name_for_clone
| mysql -u[username] -p[password] new_database_name
此语句在 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
使用 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
除了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
如果您的原始数据库中有触发器,则可以通过在导入之前通过管道替换来避免“触发器已存在”错误:
mysqldump -u olddbuser -p -d olddbname | sed "s/`olddbname`./`newdbname`./" | mysql -u newdbuser -p -D newdbname
使用 MySQL Workbench,您可以使用 Database > Migration Wizard 将数据库复制到相同或其他服务器实例。我相信它可以在服务器端工作,因此它应该是复制大型数据库的一个很好的解决方案。
你可以做:
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 是正确的方法,但这是一种在许多情况下有用的快速而肮脏的解决方案)