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


154

Je voudrais écrire un script qui copie ma base sitedb1de données actuelle sitedb2sur la même instance de base de données mysql. Je sais que je peux vider le sitedb1 dans un script SQL:

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

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


Possible duplication de la base de données Clone MySQL
bummi

Réponses:


303

Comme le manuel le dit dans Copie de bases de données, vous pouvez diriger le vidage 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 douteux.

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

Les deux commandes mysqldumpet mysqlacceptent des options pour définir les détails de connexion (et bien plus encore), comme:

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).


2
Un peu ... cela saute beaucoup d'E / S disque car vous n'avez pas à lire / écrire les données deux fois
Greg

8
Si votre base de données a une taille de gigaoctets, cela ne vous rapportera probablement pas beaucoup. Je pense que ce que l'OP veut en venir, c'est qu'il ne veut pas externaliser la copie: cela peut-il être fait uniquement dans mysql?
cletus

3
Je dirais que plus la base de données est grande, plus elle vous gagne ... Il n'y a aucun moyen de faire cela dans MySQL afaik (sauf à la main, une table / vue à la fois)
Greg

41
J'ai d'abord dû créer new_db en utilisant la commande mysql standard: "CREATE DATABASE new_db;" puis utilisé ces commandes: mysqldump -u root -p old_db | mysql -u root -p new_db
valentt

4
Cela ne fonctionne pas pour moi, si je dois mettre le mot de passe pour le dumping et l' importation comme ceci: mysqldump -uroot -p database1 | mysql -uroot -p database2. Je suis invité pour les deux pws mais je ne peux en mettre qu'un. Les regards rapides comme ceci: Enter password: Enter password: . Après avoir donné le premier pw, le processus attend indéfiniment.
Torsten

66

Utilisation des utilitaires MySQL

Les utilitaires MySQL contiennent le bel outil mysqldbcopyqui copie par défaut une base de données comprenant tous les objets associés («tables, vues, déclencheurs, événements, procédures, fonctions et attributions au niveau de la base de données») et les données d'un serveur de base de données vers le même ou vers un autre Serveur de base de données. Il existe de nombreuses 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

1
Cela a bien fonctionné pour moi, la mysqldumpsolution basée échouait.
saji89

1
Dans mon cas, j'ai dû spécifier le port comme ceci: --source = root: your_password @ localhost: 3307 (sinon cela me donnerait une erreur d'accès refusé)
pbz

4
Besoin sudo apt-get install mysql-utilities, mais c'est très soigné. Puis-je omettre le mot de passe et être invité à le saisir?
ADTC

2
@ADTC Je ne sais pas s'il existe un moyen intégré de mysqldbcopyvous demander le mot de passe; au moins, je n'ai rien trouvé de tel dans la documentation. Vous pouvez cependant créer cette fonctionnalité vous-même. Dans Bash, cela pourrait ressembler un peu à ceci:mysqldbcopy --source=root:"$(read -sp 'Source password: ' && echo $REPLY)"@localhost --destination=root:"$(read -sp 'Destination password: ' && echo $REPLY)"@localhost sitedb1:sitedb2
Chriki

1
FYI: Il semble que la commande de Chriki fonctionne parfaitement. Je devais juste ajouter --forceà la mysqldbcopycommande car j'avais déjà créé la base de données de destination. Merci!
Niavlys

19
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

2
Qu'est-ce que cela ajoute à la réponse acceptée? Est similaire, mais vous ajoutez quelques différences, ajoutez quelques commentaires pour une meilleure compréhension
Yaroslav

Cela devrait être la réponse acceptée, car elle créera la base de données, également bonne pour l'authentification. la réponse acceptée actuelle vous indiquera l'accès refusé, alors la table n'existe pas.
Rami Dabain

14

Vous devez exécuter la commande depuis le terminal / l'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'


J'aime cette réponse, elle est nette. Cependant, pour moi, mysql exigeait -p avant le mot de passe.
lwitzel le

1
Comment pouvons-nous également copier des fonctions, des événements, etc. créés dans la base de données d'origine? Cela ne regarde que les tableaux.
Dogan Askan

12

Le meilleur et le plus simple consiste à entrer ces commandes dans votre terminal et à définir les autorisations de 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

1
La question indiquait explicitement que la méthode d'exportation / importation est déjà connue.
lav

3
C'est la meilleure façon de procéder. Fonctionne également avec de grandes bases de données, alors que la version canalisée mysqldump -u <user> -p <pwd> db_name | mysql -u <user> -p <pwd> new_db_namepeut être problématique avec de grandes bases de données.
Alex le

10

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 préserver les index. Bien sûr, cela ne copie que les tableaux. Les vues et les procédures ne sont pas copiées, bien que cela puisse être fait de la même manière.

Voir CRÉER UNE TABLE .


3
Cela pourrait échouer sur l'intégrité de la référence car les tables dépendantes ne pouvaient pas encore être copiées. Peut-être que cela pourrait fonctionner en une seule grosse transaction.
Ondrej Galbavý

4

Créez d'abord la base de données dupliquée:

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;

2

Vous pouvez faire quelque chose comme ceci:

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

1

Cette déclaration 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. Il était prévu de permettre la mise à niveau des bases de données antérieures à la version 5.1 afin d'utiliser le codage implémenté dans la version 5.1 pour mapper les noms de base de données aux noms de répertoire de base de données. Cependant, l'utilisation de cette instruction peut entraîner la 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 dans lesquelles il est présent.

Pour effectuer la tâche de mise à niveau des noms de base de données avec le nouveau codage, utilisez plutôt ALTER DATABASE db_name UPGRADE DATA DIRECTORY NAME: http://dev.mysql.com/doc/refman/5.1/en/alter-database.html


1

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 vers". Utilisez-le et vous pouvez copier la base de données.


1

Comme mentionné dans la réponse de Greg , mysqldump db_name | mysql new_db_namec'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 cherchez à sauvegarder des données, que vous ne pouvez pas vous permettre de perdre des données (dans cette base de données ou dans d'autres), ou si vous utilisez des tables autres que innodb, vous devriez utiliser mysqldump.

Si vous cherchez quelque chose à développer, que toutes vos bases de données sont sauvegardées ailleurs et que vous êtes à l'aise pour purger et réinstaller mysql(éventuellement manuellement) lorsque tout va mal, alors j'ai peut-être la solution pour vous.

Je n'ai pas pu trouver une bonne alternative, alors j'ai construit un script pour le faire moi-même. J'ai passé beaucoup de temps à faire fonctionner cela la première fois et honnêtement, cela me terrifie un peu d'y apporter des modifications maintenant. Les bases de données Innodb n'étaient pas destinées à être copiées et collées de cette manière. De petits changements provoquent un échec magnifique. Je n'ai pas eu de problème depuis que j'ai finalisé le 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 sudoprivilège 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 vers une nouvelle base de données
  5. Passe 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. Clone les 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. Sort du mode de récupération pour innodb
  12. Redémarre mysql
  13. Donne aux utilisateurs mysql l'accès à la base de données
  14. Nettoie les fichiers temporaires

Comment ça se compare avec mysqldump

Sur une base de données de 3 Go, l'utilisation de mysqldumpet mysqlprendrait 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

Nous avons nos modifications SQL 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'erreurs. Un problème que nous avons rencontré était lorsque nous travaillions sur un projet à long terme avec des changements de base de données, et que nous devions changer de branche au milieu pour corriger un bogue ou trois.

Dans le passé, nous utilisions une seule base de données pour toutes les branches et devions reconstruire la base de données chaque fois que nous passions à une branche qui n'était pas compatible avec les nouvelles modifications de la base de données. Et lorsque nous reviendrions, nous devions exécuter à nouveau les mises à niveau.

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

Cette solution a réduit le temps de clonage de la base de données à 1/5 du temps (pensez à une pause café et salle de bain au lieu d'un long déjeuner).

Tâches courantes et leur temps

Le basculement entre les branches avec des modifications de base de données incompatibles prend plus de 50 minutes sur une seule base de données, mais pas du tout après l'heure de configuration initiale avec mysqldumpou ce code. Ce code se trouve être ~ 5 fois plus rapide que mysqldump.

Voici quelques tâches courantes et le temps approximatif qu'elles prendraient avec chaque méthode:

Créez une branche de fonctionnalités 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és avec les modifications de la base de données, passez à masterune correction de bogue, apportez une modification sur la branche de fonctionnalités 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 les modifications de la base de données, passez à master5 fois pour une correction de bogue tout en apportant des modifications sur la branche de fonctionnalité entre les deux, et 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 sans 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:

Capture d'écran de la sortie du script pour l'exemple de base de données


0

En plus de la réponse de Greg , c'est le moyen le plus simple et le plus rapide si le new_db_namen'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

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 tuyau de remplacement avant l'importation:

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

-4

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

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.