Клонирование базы данных MySQL в одном экземпляре MySql

Я хотел бы написать script, который копирует мою текущую базу данных sitedb1 в sitedb2 в том же экземпляре базы данных mysql. Я знаю, что могу сбросить sitedb1 в sql script:

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

а затем импортируйте его в sitedb2. Есть ли более простой способ, не сбрасывая первую базу данных в файл sql?

Ответ 1

Как говорится в руководстве Копирование баз данных, вы можете отправить дамп непосредственно в клиент 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).

Ответ 2

Использование служебных программ MySQL

Утилиты MySQL содержат хороший инструмент mysqldbcopy, который по умолчанию копирует БД, включая все связанные объекты ( "таблицы, представления, триггеры, события, процедуры, функции, и гранты на уровне базы данных" ) и данные с одного сервера БД на тот же или на другой сервер БД. Существует множество опций для настройки того, что на самом деле скопировано.

Итак, чтобы ответить на вопрос OPs:

mysqldbcopy \
    --source=root:[email protected] \
    --destination=root:[email protected] \
    sitedb1:sitedb2

Ответ 3

$ 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

Ответ 4

Вам нужно запустить команду из командной строки терминала/команды.

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'

Ответ 5

Лучший и простой способ - ввести эти команды в свой терминал и установить разрешения для пользователя root. Работает на меня..!

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

Ответ 6

Вы можете использовать (в псевдокоде):

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.

Ответ 7

Сначала создайте дублируемую базу данных:

CREATE DATABASE duplicateddb;

Убедитесь, что разрешения и т.д. Все на месте и:

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

Ответ 8

Вы можете сделать что-то вроде следующего:

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

Ответ 9

Этот оператор был добавлен в 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

Ответ 10

Лучше использовать команду mysqldbcopy для копирования базы данных с одного на другой сервер или тот же сервер.

mysqldbcopy --source=root:[email protected] --destination=root:[email protected] database-name:database-name-clone

[MySQL]

Ответ 11

Простой способ сделать это, если вы установили phpmyadmin:

Перейдите в свою базу данных, выберите вкладку "операция", и вы можете увидеть блок "копировать базу данных". Используйте его, и вы можете скопировать базу данных.

Ответ 12

В дополнение к ответу Грега, это самый простой и быстрый способ, если 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

Ответ 13

Используйте "mysqldbcopy" на терминале. Этот случай хорошо упоминается здесь. Пример. Запустите команду cmd. Перейдите в папку bin mySql Server. Выполните следующий запрос:

C:\Program Files\MySQL\MySQL Server 5.7\bin>mysqldbcopy --source=root:[email protected] --destination=root:[email protected] master:master_clone

здесь я пытаюсь копировать свой "master" db в "master_clone" на localhost.

Ответ 14

Если у вас есть триггеры в исходной базе данных, вы можете избежать ошибки "Триггер уже существует", передав замену перед импортом:

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

Ответ 15

Как упоминалось в Greg answer, mysqldump db_name | mysql new_db_name - это бесплатный, безопасный и простой способ переноса данных между базами данных. Тем не менее, это также очень медленно.

Если вы ищете для резервного копирования данных, не можете позволить себе потерять данные (в этой или других базах данных) или используете таблицы, отличные от innodb, то вам следует использовать mysqldump.

Если вы ищете что-то для разработки, резервное копирование всех ваших баз данных в другом месте, и вам удобно чистить и переустанавливать mysql (возможно, вручную), когда все идет не так, то я могу просто найти решение для вас.

Я не мог найти хорошую альтернативу, поэтому я создал сценарий, чтобы сделать это сам. Я потратил много времени, чтобы заставить это работать с первого раза, и это, честно говоря, немного пугает меня, чтобы внести изменения в него сейчас. Базы данных Innodb не предназначались для копирования и вставки таким образом. Небольшие изменения приводят к тому, что это выходит из строя великолепными способами. У меня не было проблем с тех пор, как я доработал код, но это не значит, что вы не будете.

Системы протестированы (но могут все еще не работать):

  • Ubuntu 16.04, mysql по умолчанию, innodb, отдельные файлы на таблицу
  • Ubuntu 18.04, mysql по умолчанию, innodb, отдельные файлы для таблицы

Что он делает

  1. Получает привилегию sudo и проверяет, достаточно ли у вас места для клонирования базы данных
  2. Получает привилегии root для mysql
  3. Создает новую базу данных с именем текущей ветки git
  4. Структура клонов в новой базе данных
  5. Переключение в режим восстановления для innodb
  6. Удаляет данные по умолчанию в новой базе данных
  7. Останавливает MySQL
  8. Клонирует данные в новую базу данных
  9. Запускает MySQL
  10. Связывает импортированные данные в новую базу данных
  11. Выход из режима восстановления для innodb
  12. Перезапускает MySQL
  13. Предоставляет пользователю mysql доступ к базе данных
  14. Очищает временные файлы

Как это сравнивается с mysqldump

В базе данных объемом 3 ГБ использование mysqldump и mysql займет 40-50 минут на моей машине. Используя этот метод, тот же процесс займет всего ~ 8 минут.

Как мы используем это

Мы сохранили изменения SQL вместе с нашим кодом, и процесс обновления автоматизирован как на производстве, так и на разработке, причем каждый набор изменений создает резервную копию базы данных для восстановления в случае возникновения ошибок. Одна из проблем, с которой мы столкнулись, заключалась в том, что мы работали над долгосрочным проектом с изменениями в базе данных и должны были переключаться между ветками, чтобы исправить одну или три ошибки.

В прошлом мы использовали единую базу данных для всех ветвей, и нам приходилось перестраивать базу данных всякий раз, когда мы переключались на ветку, которая не была совместима с новыми изменениями базы данных. И когда мы вернемся назад, нам придется снова запускать обновления.

Мы попытались 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 [email protected]" | 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

Если все идет гладко, вы должны увидеть что-то вроде:

Screenshot of script output for example database

Ответ 16

Я не думаю, что есть способ сделать это. Когда PHPMyAdmin делает это, он выгружает БД, а затем повторно вставляет его под новым именем.