A clonagem de um banco de dados MySQL no mesmo exemplo MySQL
Pergunta
Eu gostaria de escrever um script que copia o meu sitedb1
banco de dados atual para sitedb2
na mesma instância de banco de dados mysql. Eu sei que posso despejar o sitedb1 a um script sql:
mysqldump -u root -p sitedb1 >~/db_name.sql
e, em seguida, importá-lo para sitedb2
.
Existe uma maneira mais fácil, sem despejar o primeiro banco de dados para um arquivo sql?
Solução
Como o manual diz em Copiando bancos de dados você tubo de lata o despejo diretamente no cliente mysql:
mysqldump db_name | mysql new_db_name
Se você estiver usando MyISAM você pode copiar os arquivos, mas eu não recomendo. É um pouco duvidosos.
Integrado de vários bons outras respostas
Ambos os comandos mysqldump
e mysql
aceitar opções para definir detalhes da ligação (e muito mais), como:
mysqldump -u <user name> --password=<pwd> <original db> | mysql -u <user name> -p <new db>
Além disso, se o novo banco de dados não está existindo, no entanto, você tem que criá-lo com antecedência (por exemplo, com echo "create database new_db_name" | mysql -u <dbuser> -p
).
Outras dicas
Usando MySQL Utilities
Os Utilities MySQL conter a boa ferramenta mysqldbcopy
que por cópias padrão de um DB incluindo todos os objetos relacionados ( “tabelas, views, triggers, eventos, procedimentos, funções e subsídios em nível de banco de dados”) e os dados de um servidor de banco de dados para o mesmo ou para outro servidor de DB. Há muitas opções disponíveis para personalizar o que realmente está copiado.
Assim, para responder à pergunta do OP:
mysqldbcopy \
--source=root:your_password@localhost \
--destination=root:your_password@localhost \
sitedb1:sitedb2
$ 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
Você precisa executar o comando do terminal / prompt de comando.
mysqldump -u <user name> -p <pwd> <original db> | mysql -u <user name> <pwd> <new db>
por exemplo: mysqldump -u root test_db1 | mysql -u root test_db2
Esta cópias test_db1 para test_db2 e conceder o acesso ao 'root' @ 'localhost'
Best e fácil maneira é entrar nesses comandos em suas permissões de terminais e definido para o usuário root. Funciona para mim..!
:~$> mysqldump -u root -p db1 > dump.sql
:~$> mysqladmin -u root -p create db2
:~$> mysql -u root -p db2 < dump.sql
Você pode usar (em pseudocódigo):
FOREACH tbl IN db_a:
CREATE TABLE db_b.tbl LIKE db_a.tbl;
INSERT INTO db_b.tbl SELECT * FROM db_a.tbl;
A razão que eu não estou usando o CREATE TABLE ... SELECT ... sintaxe é preservar índices. Claro que isso só copia tabelas. Exibições e procedimentos não são copiadas, embora possa ser feito da mesma maneira.
Primeiro, crie o banco de dados duplicado:
CREATE DATABASE duplicateddb;
Certifique-se as permissões etc estão todos no lugar e:
mysqldump -u admin -p originaldb | mysql -u backup -p password duplicateddb;
Você pode fazer algo como o seguinte:
mysqldump -u[username] -p[password] database_name_for_clone
| mysql -u[username] -p[password] new_database_name
Esta declaração foi adicionada no MySQL 5.1.7, mas foi encontrado para ser perigoso e foi removido no MySQL 5.1.23. Foi destinado a permitir a modernização pré-5.1 bancos de dados para usar a codificação implementado em 5.1 para nomes de banco de dados de mapeamento para nomes de diretório de banco de dados. No entanto, o uso desta declaração pode resultar em perda de conteúdo banco de dados, razão pela qual ele foi removido. Não use RENAME DATABASE em versões anteriores em que está presente.
Para executar a tarefa de nomes de banco de dados atualização com a nova codificação, utilize ALTER DATABASE db_name UPGRADE dados do diretório NOME vez: http://dev.mysql.com/doc/refman/5.1/en/alter-database.html
Uma maneira simples de fazê-lo se você tiver instalado phpMyAdmin :
Vá para o seu banco de dados, selecione a guia "operação", e você pode ver o "banco de dados de cópia para" bloco. Use-o e você pode copiar o banco de dados.
Além de Greg resposta , esta é a maneira mais fácil e mais rápido se o new_db_name
ainda não existe:
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
Se você tem gatilhos em seu banco de dados original, você pode evitar o "Trigger já existe" erro canalizando um substituto antes da importação:
mysqldump -u olddbuser -p -d olddbname | sed "s/`olddbname`./`newdbname`./" | mysql -u newdbuser -p -D newdbname
Conforme mencionado na de Greg resposta , mysqldump db_name | mysql new_db_name
é a livre seguro e fácil maneira, para transferir dados entre os bancos de dados. No entanto, também é muito lento .
Se você estiver olhando para os dados de backup, não pode dar ao luxo de perder dados (neste ou outros bancos de dados), ou se estiver usando diferentes innodb
tabelas, então você deve usar mysqldump
.
Se você está procurando algo para o desenvolvimento, tem todos os seus bancos de dados de backup em outro lugar, e são purga confortável e mysql
reinstalar (possivelmente manualmente) quando tudo der errado, então eu só poderia ter a solução para você.
Eu não poderia encontrar uma boa alternativa, assim que eu construí um script para fazer isso sozinho. Passei muito de tempo começar este trabalho pela primeira vez e honestamente me assusta um pouco para fazer alterações agora. bancos de dados InnoDB não foram feitos para copiado e colado assim. Pequenas mudanças causar esta a falhar de forma magnífica. Eu não tive um problema desde que eu finalizado o código, mas isso não significa que você não vai.
Sistemas testado em (mas ainda pode falhar em):
- Ubuntu 16.04, mysql padrão, InnoDB, arquivos separados por tabela
- Ubuntu 18.04, mysql padrão, InnoDB, arquivos separados por tabela
O que ele faz
- Obtém privilégio
sudo
e verifica que você tem espaço de armazenamento suficiente para clonar o banco de dados - Obtém privilégios mysql root
- Cria um novo banco de dados nomeado após o git branch atual
- estrutura Clones a nova base de dados
- muda para o modo de recuperação para innodb
- dados padrão Exclui em novo banco de dados
- Pára mysql
- Dados clones para novo banco de dados
- Inicia mysql
- Data Links importados no novo banco de dados
- Muda do modo de recuperação para innodb
- Reinicia mysql
- Dá acesso do usuário mysql para banco de dados
- Limpa arquivos temporários
Como ele se compara com mysqldump
Em um banco de dados de 3GB, usando mysqldump
e mysql
levaria 40-50 minutos na minha máquina. Usando este método, o mesmo processo levaria apenas ~ 8 minutos.
Como podemos usá-lo
Nós temos nossa SQL alterações salvas junto com o nosso código e o processo de atualização é automatizado na produção e desenvolvimento, com cada conjunto de mudanças que fazem um backup do banco de dados para restaurar se há erros. Um problema que tive foi quando estávamos trabalhando em um projeto de longo prazo com mudanças de banco de dados, e teve que ramos interruptor no meio dela para corrigir um bug ou três.
No passado, foi utilizado um único banco de dados para todos os ramos, e teria de reconstruir o banco de dados sempre que nós mudamos para um ramo que não era compatível com as novas alterações de banco de dados. E quando voltou, teríamos de executar as atualizações novamente.
Nós tentamos mysqldump
para duplicar a base de dados para diferentes ramos, mas o tempo de espera foi muito tempo (40-50 minutos), e nós não poderíamos fazer qualquer outra coisa nesse meio tempo.
Esta solução reduziu o tempo clone banco de dados para 1/5 o tempo (pense café e casa de banho pausa em vez de um longo almoço).
Tarefas comuns e seu tempo
A comutação entre filiais com alterações de dados incompatíveis leva 50+ minutos em um único banco de dados, mas pouco tempo depois do tempo de configuração inicial com mysqldump
ou este código. Este código só acontece de ser ~ 5 vezes mais rápido que mysqldump
.
Aqui estão algumas tarefas comuns e cerca de quanto tempo eles levariam com cada método:
Criar ramo de funcionalidade com alterações de dados e mesclar imediatamente:
- banco de dados único: ~ 5 minutos
- Clone com
mysqldump
: 50-60 minutos - Clone com este código: ~ 18 minutos
Criar ramo de funcionalidade com alterações de dados, mude para master
parauma correção de bug fazer uma edição no ramo de funcionalidade, e merge:
- banco de dados único: ~ 60 minutos
- Clone com
mysqldump
: 50-60 minutos - Clone com este código: ~ 18 minutos
Criar ramo de funcionalidade com alterações de dados, mude para master
para um bugfix 5 vezes ao fazer edições no inbetween recurso ramo, e merge:
- banco de dados único: ~ 4 horas, 40 minutos
- Clone com
mysqldump
: 50-60 minutos - Clone com este código: ~ 18 minutos
O código
Não use este a menos que você leu e entendeu tudo acima.
#!/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
Se tudo correr bem, você deve ver algo como:
Eu não acho que há um método para fazer isso. Quando PHPMyAdmin faz isso, ele despeja a DB, em seguida, re-inserções lo sob o novo nome.