同じMySqlインスタンスでのMySQLデータベースの複製


154

現在のデータベースsitedb1sitedb2同じmysqlデータベースインスタンスにコピーするスクリプトを書きたいのですが。sitedb1をSQLスクリプトにダンプできることはわかっています。

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

にインポートしsitedb2ます。最初のデータベースをSQLファイルにダンプせずに簡単な方法はありますか?


回答:


302

マニュアルのデータベースのコピーで述べているように、mysqlクライアントに直接ダンプをパイプすることができます。

mysqldump db_name | mysql new_db_name

MyISAMを使用している場合はファイルをコピーできますが、お勧めしません。それは少し危険です。

さまざまな良い他の回答から統合

mysqldumpmysqlコマンドはどちらも、接続の詳細を設定するためのオプションを受け入れます(以下を参照)。

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)作成する必要があります。


2
ちょっと...データを2回読み書きする必要がないので、ディスクIOの多くをスキップします
Greg

8
データベースのサイズがギガバイトの場合、これはおそらくあまり効果がありません。OPが達成しているのは、コピーを外部化したくない場合です。純粋にmysql内で実行できますか?
cletus 2009年

3
私は、DBが大きければ大きいほど、より多くの利益を得ると思います... MySQLのafaik内でこれを行う方法はありません(手動で、一度に1つのテーブル/ビューを除く)
Greg

41
最初に、標準のmysqlコマンドを使用してnew_dbを作成する必要がありました: "CREATE DATABASE new_db;"。次に、これらのコマンドを使用しました:mysqldump -u root -p old_db | mysql -u root -p new_db
valentt

4
ダンプとインポートのために次のようにパスワードを入力する必要がある場合、これは私にとっては機能しませんmysqldump -uroot -p database1 | mysql -uroot -p database2。両方のパスワードの入力を求められますが、入力できるのは1つだけです。プロンプトは次のようになりますEnter password: Enter password: 。最初のパスワードを与えた後、プロセスは永久に待機します。
Torsten

66

MySQLユーティリティの使用

MySQLユーティリティには素晴らしいツールが含まれています mysqldbcopyデフォルトですべての関連オブジェクト(「テーブル、ビュー、トリガー、イベント、プロシージャ、関数、およびデータベースレベルの付与」)を含むDBおよびデータを1つのDBサーバーから同じサーバーまたは別のサーバーにコピーするがれていますDBサーバー。実際にコピーされるものをカスタマイズするために利用できるオプションはたくさんあります。

したがって、OPの質問に答えるには:

mysqldbcopy \
    --source=root:your_password@localhost \
    --destination=root:your_password@localhost \
    sitedb1:sitedb2

1
これは私にとってはうまくいき、mysqldumpベースのソリューションは失敗していました。
saji89

1
私の場合、次のようにポートを指定する必要がありました:
source

4
する必要がsudo apt-get install mysql-utilitiesありますが、これは非常にきちんとしています。パスワードを省略して、それを入力するように求められますか?
ADTC 2016年

2
@ADTC mysqldbcopyパスワードを尋ねる組み込みの方法があるかどうかわかりません。少なくとも私はドキュメントでそのようなものを見つけることができませんでした。ただし、この機能を自分で構築することもできます。:バッシュではややこのようになります。そのmysqldbcopy --source=root:"$(read -sp 'Source password: ' && echo $REPLY)"@localhost --destination=root:"$(read -sp 'Destination password: ' && echo $REPLY)"@localhost sitedb1:sitedb2
Chriki

1
参考:Chrikiのコマンドは完璧に機能しているようです。宛先データベースを既に作成しているため--forcemysqldbcopyコマンドに追加する必要がありました。ありがとう!
Niavlys 2017年

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
それは受け入れられた答えに何を追加しますか?似ていますが、理解を深めるためにいくつかの違いを追加し、コメントを追加します
Yaroslav

データベースを作成するため、これは承認された回答である必要があり、認証にも適しています。現在受け入れられている回答は、アクセスが拒否されたことを通知し、テーブルは存在しません。
Rami Dabain 16年

14

ターミナル/コマンドプロンプトからコマンドを実行する必要があります。

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'へのアクセスを許可します


私はこの答えが好きです、それはパリパリです。ただし、私にとってmysqlはパスワードの前に-pを必要としました。
lwitzel 2017

1
元のデータベースで作成された関数やイベントなどをどのようにコピーできますか?これはテーブルをコピーするだけです。
Dogan Askan

12

最善かつ簡単な方法は、ターミナルにこれらのコマンドを入力し、rootユーザーに権限を設定することです。私のために働く..!

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

1
質問は、エクスポート/インポート方法がすでに知られていることを明確に述べました。
lav

3
これが最良の方法です。大規模なデータベースでも機能しますが、パイプバージョンmysqldump -u <user> -p <pwd> db_name | mysql -u <user> -p <pwd> new_db_nameは大規模なデータベースでは問題になる可能性があります。
Alex

10

あなたは(疑似コードで)使用することができます:

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 ...構文を使用しない理由は、インデックスを保持するためです。もちろん、これはテーブルのみをコピーします。ビューとプロシージャはコピーされませんが、同じ方法で実行できます。

CREATE TABLEを参照してください


3
依存テーブルはまだコピーできなかったため、これは参照整合性で失敗する可能性があります。たぶん、1つの大きなトランザクションで機能する可能性があります。
OndrejGalbavý2017

4

最初に複製データベースを作成します。

CREATE DATABASE duplicateddb;

権限などがすべて整っていることを確認してください:

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


1

このステートメントはMySQL 5.1.7で追加されましたが、危険であることが判明し、MySQL 5.1.23で削除されました。これは、5.1より前のデータベースをアップグレードして、データベース名をデータベースディレクトリ名にマッピングするために5.1で実装されたエンコーディングを使用できるようにすることを目的としています。ただし、このステートメントを使用すると、データベースの内容が失われる可能性があるため、削除されました。RENAME DATABASEが存在する以前のバージョンでは、RENAME DATABASEを使用しないでください。

新しいエンコーディングでデータベース名をアップグレードするタスクを実行するには、代わりにALTER DATABASE db_name UPGRADE DATA DIRECTORY NAMEを使用します。 。http //dev.mysql.com/doc/refman/5.1/en/alter-database.html


1

phpmyadminをインストールした場合の簡単な方法

データベースに移動し、「操作」タブを選択すると、「データベースのコピー先」ブロックが表示されます。それを使用すると、データベースをコピーできます。


1

グレッグの回答で述べたように、mysqldump db_name | mysql new_db_nameはデータベース間でデータを転送するための無料で安全かつ簡単な方法です。しかし、それも本当に遅いです。

データのバックアップを検討している場合、(このデータベースまたは他のデータベースの)データを失う余裕がない場合、または以外のテーブルinnodbを使用してmysqldumpいる場合は、を使用する必要があります。

開発用の何かを探している場合、すべてのデータベースを別の場所にバックアップしmysql、すべてがうまくいかない場合は(おそらく手動で)パージして再インストールしてもかまいません。解決策があるかもしれません。

良い代替案が見つからなかったので、自分でスクリプトを作成しました。私はたくさん過ごしましこれを最初に機能させるの時間そしてそれは今それを変更することを正直に少し怖がらせます。Innodbデータベースは、このようにコピーして貼り付けることを意図していませんでした。小さな変更はこれを壮大な方法で失敗させます。コードを完成させて以来、問題はありませんでしたが、だからといって問題がないわけではありません。

テストされたシステム(ただし、失敗する可能性があります):

  • Ubuntu 16.04、デフォルトのmysql、innodb、テーブルごとに個別のファイル
  • Ubuntu 18.04、デフォルトのmysql、innodb、テーブルごとに個別のファイル

それがすること

  1. sudo権限を取得し、データベースを複製するのに十分なストレージ領域があることを確認します
  2. ルートMySQL権限を取得します
  3. 現在のgitブランチにちなんで名付けられた新しいデータベースを作成します
  4. 新しいデータベースへの構造のクローン
  5. innodbをリカバリモードに切り替えます
  6. 新しいデータベースのデフォルトデータを削除します
  7. mysqlを停止します
  8. データを新しいデータベースに複製
  9. mysqlを起動します
  10. インポートされたデータを新しいデータベースにリンクする
  11. innodbのリカバリーモードを終了します
  12. mysqlを再起動します
  13. mysqlユーザーにデータベースへのアクセス権を付与します
  14. 一時ファイルをクリーンアップします

それとの比較 mysqldump

3 GBのデータベースで、を使用するmysqldumpmysql、私のマシンで40〜50分かかります。この方法を使用すると、同じプロセスに約8分しかかかりません。

使い方

SQLの変更はコードと一緒に保存され、アップグレードプロセスはプロダクションと開発の両方で自動化されます。変更のセットごとに、データベースのバックアップが作成され、エラーが発生した場合に復元されます。私たちが遭遇した1つの問題は、データベースの変更を伴う長期プロジェクトに取り組んでいて、その途中でブランチを切り替えてバグを1つまたは3つ修正する必要があったことです。

以前は、すべてのブランチに単一のデータベースを使用しており、新しいデータベースの変更と互換性のないブランチに切り替えるたびにデータベースを再構築する必要がありました。元に戻したときには、アップグレードを再度実行する必要があります。

mysqldump異なるブランチに対してデータベースを複製しようとしましたが、待ち時間が長すぎ(40〜50分)、その間他には何もできませんでした。

このソリューションにより、データベースのクローン作成時間が1/5に短縮されました(長い昼食の代わりに、コーヒーとバスルームの休憩を考えてください)。

一般的なタスクとその時間

互換性のないデータベースの変更があるブランチ間の切り替えには、単一のデータベースで50分以上かかりますが、mysqldumpまたはこのコードを使用した初期セットアップ時間後はまったく時間はかかりません。このコードはたまたまの5倍程度高速ですmysqldump

ここでは、いくつかの一般的なタスクと、各メソッドで大まかにかかる時間を示します。

データベースを変更して機能ブランチを作成し、すぐにマージします。

  • 単一データベース:約5分
  • クローンmysqldump:50-60分
  • このコードでクローンを作成する:〜18分

データベースの変更を伴う機能ブランチを作成masterし、バグ修正に切り替えて、機能ブランチを編集し、マージします。

  • 単一データベース:約60分
  • クローンmysqldump:50-60分
  • このコードでクローンを作成する:〜18分

データベースが変更さmasterれた機能ブランチを作成し、その間に機能ブランチを編集しながら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

すべてが順調に進んだら、次のように表示されます。

サンプルデータベースのスクリプト出力のスクリーンショット


0

グレッグの回答に加えて、これがnew_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

0

元のデータベースにトリガーがある場合は、インポートの前に置換をパイプすることで、「トリガーはすでに存在しています」エラーを回避できます。

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

-4

これを行う方法はないと思います。PHPMyAdminがこれを行うと、DBがダンプされ、新しい名前で再挿入されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.