web-dev-qa-db-fra.com

Cloner une base de données MySQL sur la même instance MySql

J'aimerais écrire un script qui copie ma base de données actuelle sitedb1 dans sitedb2 sur la même instance de base de données mysql. Je sais que je peux transférer le fichier sitedb1 dans un script SQL:

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

puis importez-le dans sitedb2. Y at-il un moyen plus facile, sans vider la première base de données dans un fichier SQL?

103
uclajatt

Comme le dit le manuel dans Copier des bases de données vous pouvez diriger le dump directement dans le client mysql:

mysqldump db_name | mysql new_db_name

Si vous utilisez MyISAM, vous pouvez copier les fichiers, mais je ne le recommanderais pas. C'est un peu louche.

Intégré à partir de diverses bonnes autres réponses

Les commandes mysqldump et mysql acceptent les options permettant de définir les détails de connexion (et bien plus encore), telles que:

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

De plus, si la nouvelle base de données n’existe pas encore, vous devez la créer au préalable (par exemple avec echo "create database new_db_name" | mysql -u <dbuser> -p).

215
Greg

Utiliser les utilitaires MySQL

Les utilitaires MySQL contiennent l’outil Nice mysqldbcopy qui copie par défaut une base de données, y compris tous les objets associés ("tables, vues, déclencheurs, événements, procédures, fonctions et attributions au niveau de la base de données") identique ou à un autre serveur de base de données. Il y a beaucoup d'options disponibles pour personnaliser ce qui est réellement copié.

Donc, pour répondre à la question du PO:

mysqldbcopy \
    --source=root:your_password@localhost \
    --destination=root:your_password@localhost \
    sitedb1:sitedb2
51
Chriki
$ 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
16
Peru

Vous devez exécuter la commande à partir du terminal/invite de commande. 

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

par exemple: mysqldump -u root test_db1 | mysql -u root test_db2

Ceci copie test_db1 dans test_db2 et accorde l’accès à 'root' @ 'localhost'

12
Srikanth Gurram

Vous pouvez utiliser (en pseudocode):

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

La raison pour laquelle je n'utilise pas la syntaxe CREATE TABLE ... SELECT ... est de conserver les index. Bien sûr, cela ne copie que les tables. Les vues et les procédures ne sont pas copiées, bien que cela puisse être fait de la même manière.

Voir CREATE TABLE .

7
Emil H

Le meilleur moyen consiste à entrer ces commandes dans votre terminal et à définir des autorisations pour l'utilisateur root. Travaille pour moi..! 

:~$> mysqldump -u root -p db1 > dump.sql
:~$> mysqladmin -u root -p create db2
:~$> mysql -u root -p db2 < dump.sql
5
Naseer-shaik

Commencez par créer la base de données en double:

CREATE DATABASE duplicateddb;

Assurez-vous que les autorisations, etc. sont toutes en place et:

mysqldump -u admin -p originaldb | mysql -u backup -p password duplicateddb;
4
svg

Vous pouvez faire quelque chose comme ce qui suit:

mysqldump -u[username] -p[password] database_name_for_clone 
 | mysql -u[username] -p[password] new_database_name
2
Digambar Patil

Cette instruction a été ajoutée dans MySQL 5.1.7 mais s'est avérée dangereuse et a été supprimée dans MySQL 5.1.23. L'objectif était de permettre à la mise à niveau des bases de données antérieures à la version 5.1 d'utiliser le codage implémenté dans la version 5.1 pour mapper les noms de bases de données sur les noms de répertoires de bases de données. Toutefois, l'utilisation de cette instruction peut entraîner une perte du contenu de la base de données, raison pour laquelle elle a été supprimée. N'utilisez pas RENAME DATABASE dans les versions antérieures où il est présent.

Pour effectuer la mise à niveau des noms de base de données avec le nouveau codage, utilisez ALTER DATABASE nom_bdd UPGRADE DATA DIRECTORY NAME à la place: http://dev.mysql.com/doc/refman/5.1/fr/alter-database.html

1
zacheusz

Il est préférable d'utiliser la commande mysqldbcopy pour copier la base de données d'un serveur à un autre ou au même serveur.

mysqldbcopy --source=root:root@localhost --destination=root:root@localhost database-name:database-name-clone

[MySQL]

1
Rakesh C

Un moyen simple de le faire si vous avez installé phpmyadmin :

Allez dans votre base de données, sélectionnez l'onglet "opération", et vous pouvez voir le bloc "copier la base de données dans" Utilisez-le et vous pouvez copier la base de données.

0
fzyzcjy

Comme mentionné dans réponse de Greg , mysqldump db_name | mysql new_db_name est le moyen gratuit, sûr et facile de transférer des données entre des bases de données. Cependant, c'est aussi très lent.

Si vous souhaitez sauvegarder des données, si vous ne pouvez pas vous permettre de perdre des données (dans cette base de données ou dans d'autres), ou utilisez des tables autres que innodb, vous devez utiliser mysqldump.

Si vous recherchez quelque chose à développer, que toutes vos bases de données soient sauvegardées ailleurs et que vous pouvez facilement purger et réinstaller mysql (éventuellement manuellement) lorsque tout va mal, je pourrais peut-être trouver la solution.

Je ne pouvais pas trouver une bonne alternative, alors j'ai construit un script pour le faire moi-même. J'ai passé beaucoup de temps à obtenir que cela fonctionne la première fois et honnêtement, cela me terrifie un peu de le modifier maintenant. Les bases de données Innodb n'étaient pas destinées à être copiées et collées de la sorte. De petits changements entraînent un échec magnifique. Je n'ai pas eu de problème depuis la finalisation du code, mais cela ne veut pas dire que vous ne le ferez pas.

Systèmes testés sur (mais peuvent toujours échouer):

  • Ubuntu 16.04, mysql par défaut, innodb, fichiers séparés par table
  • Ubuntu 18.04, mysql par défaut, innodb, fichiers séparés par table

Ce qu'il fait

  1. Obtient le privilège Sudo et vérifie que vous disposez de suffisamment d'espace de stockage pour cloner la base de données.
  2. Obtient les privilèges root mysql
  3. Crée une nouvelle base de données nommée d'après la branche git actuelle
  4. Cloner la structure dans une nouvelle base de données
  5. Basculer en mode de récupération pour innodb
  6. Supprime les données par défaut dans la nouvelle base de données
  7. Arrête mysql
  8. Cloner des données dans une nouvelle base de données
  9. Démarre mysql
  10. Liens des données importées dans une nouvelle base de données
  11. Quitte le mode de récupération pour innodb
  12. Redémarre mysql
  13. Permet à l'utilisateur mysql d'accéder à la base de données
  14. Nettoie les fichiers temporaires

Comment cela se compare-t-il avec mysqldump

Sur une base de données de 3 Go, utiliser mysqldump et mysql prendrait 40 à 50 minutes sur ma machine. En utilisant cette méthode, le même processus ne prendrait que 8 minutes environ.

Comment nous l'utilisons

Nos modifications SQL sont enregistrées avec notre code et le processus de mise à niveau est automatisé à la fois en production et en développement, chaque ensemble de modifications effectuant une sauvegarde de la base de données à restaurer en cas d'erreur. Un problème que nous avons rencontré concernait le fait que nous travaillions sur un projet à long terme avec des modifications de la base de données et que nous devions changer de branche au milieu pour résoudre un ou plusieurs bugs.

Dans le passé, nous utilisions une base de données unique pour toutes les branches et nous devions la reconstruire chaque fois que nous passions à une branche non compatible avec les nouvelles modifications de la base de données. Et lorsque nous sommes revenus, nous devions relancer les mises à niveau.

Nous avons essayé mysqldump de dupliquer la base de données pour différentes branches, mais le temps d'attente était trop long (40 à 50 minutes) et nous ne pouvions rien faire d'autre dans l'intervalle.

Cette solution a raccourci le temps de clonage de la base de données à 1/5 du temps (pensez au café et à la pause de la salle de bain au lieu d’un long repas).

Tâches communes et leur temps

La commutation entre des branches avec des modifications de base de données incompatibles prend plus de 50 minutes sur une seule base de données, mais aucune heure après la configuration initiale avec mysqldump ou ce code. Ce code est environ 5 fois plus rapide que mysqldump.

Voici quelques tâches courantes et leur durée approximative avec chaque méthode:

Créez une branche avec les modifications de la base de données et fusionnez immédiatement:

  • Base de données unique: ~ 5 minutes
  • Cloner avec mysqldump: 50 à 60 minutes
  • Cloner avec ce code: ~ 18 minutes

Créez une branche de fonctionnalité avec des modifications de base de données, passez à master pour une correction de bogue, effectuez une modification sur la branche de fonctionnalité et fusionnez:

  • Base de données unique: ~ 60 minutes
  • Cloner avec mysqldump: 50 à 60 minutes
  • Cloner avec ce code: ~ 18 minutes

Créez une branche de fonctionnalité avec des modifications de base de données, passez 5 fois à master pour une correction de bogue tout en apportant des modifications sur la branche de fonctionnalité entre-deux, puis fusionnez:

  • Base de données unique: ~ 4 heures, 40 minutes
  • Cloner avec mysqldump: 50 à 60 minutes
  • Cloner avec ce code: ~ 18 minutes

Le code

N'utilisez pas ceci à moins d'avoir lu et compris tout ce qui précède.

#!/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

Si tout se passe bien, vous devriez voir quelque chose comme:

Screenshot of script output for example database

0
0b10011

En plus de la réponse de Greg.
le moyen le plus simple et le plus rapide si le new_db_name n'existe pas encore. 

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
0
rayphi

Si vous avez des déclencheurs dans votre base de données d'origine, vous pouvez éviter l'erreur "Le déclencheur existe déjà" en envoyant un remplacement avant l'importation:

mysqldump -u olddbuser -p -d olddbname | sed "s/`olddbname`./`newdbname`./" | mysql -u newdbuser -p -D newdbname
0
zeusstl

Je ne pense pas qu'il existe une méthode pour faire cela. Lorsque cela est fait, PHPMyAdmin vide la base de données puis la réinsère sous le nouveau nom.

0
UnkwnTech

Utilisez 'mysqldbcopy' sur le terminal. Ce cas est bien mentionné ici ..__ Exemple: Démarrez l’invite cmd. Accédez au dossier bin de mySql Server. Lancez la requête ci-dessous:

C:\Program Files\MySQL\MySQL Server 5.7\bin>mysqldbcopy --source=root:root@localhost --destination=root:root@localhost master:master_clone

ici, j'essaie de copier ma base de données «master» sur «master_clone» sur localhost.

0
Rajneesh