Bahsedildiği üzere Greg'in cevap , mysqldump db_name | mysql new_db_name
olan , ücretsiz kasa ve kolay veritabanları arasında veri aktarmak için bir yol. Ancak, aynı zamanda gerçekten yavaş .
Verileri yedeklemek istiyorsanız, (bu veya diğer veritabanlarında) veri kaybetmeyi göze alamıyorsanız veya dışında tablolar kullanıyorsanız innodb
, kullanmalısınız mysqldump
.
Geliştirme için bir şey arıyorsanız, tüm veritabanlarınızı başka bir yerde yedekleyin ve rahatlıkla temizleyip yeniden kurun mysql
her şey yanlış gittiğinde (muhtemelen manuel olarak), o zaman sizin için bir çözüm olabilir.
İyi bir alternatif bulamadım, bu yüzden bunu kendim yapmak için bir senaryo oluşturdum. Bunu ilk kez çalışmak için çok zaman harcadım ve dürüstçe şu anda değişiklikler yapmak beni biraz korkutuyor. Innodb veritabanlarının kopyalanması ve yapıştırılması amaçlanmamıştır. Küçük değişiklikler bunun muhteşem bir şekilde başarısız olmasına neden olur. Kodu tamamladığımdan beri bir sorun yaşamadım ama bu yapmayacağınız anlamına gelmiyor.
Test edilen sistemler (ancak yine de başarısız olabilir):
- Ubuntu 16.04, varsayılan mysql, innodb, tablo başına ayrı dosyalar
- Ubuntu 18.04, varsayılan mysql, innodb, tablo başına ayrı dosyalar
Bu ne yapar
- Alır
sudo
veritabanını klonlamak için yeterli depolama alanı ayrıcalık ve doğrular
- Kök MySQL ayrıcalıklarını alır
- Geçerli git dalının adını taşıyan yeni bir veritabanı oluşturur
- Klonların yapısı yeni veritabanına
- Innodb için kurtarma moduna geçer
- Yeni veritabanındaki varsayılan verileri siler
- MySQL'i durdurur
- Verileri yeni veritabanına klonlar
- MySQL'i başlatır
- İçe aktarılan verileri yeni veritabanına bağlar
- Innodb için kurtarma modunu kapatır
- MySQL'i yeniden başlatır
- MySQL kullanıcı veritabanına erişim sağlar
- Geçici dosyaları temizler
İle nasıl karşılaştırılır mysqldump
3GB'lık bir veritabanında mysqldump
ve mysql
makinemde 40-50 dakika sürecek. Bu yöntemi kullanarak, aynı işlem sadece ~ 8 dakika sürecektir.
Nasıl Kullanıyoruz
SQL değişikliklerimizi kodumuzla birlikte kaydettik ve yükseltme işlemi hem üretim hem de geliştirme üzerinde otomatikleştiriliyor; her değişiklik kümesi, hatalar varsa geri yüklemek için veritabanının yedeğini alıyor. Karşılaştığımız bir sorun, veritabanı değişiklikleriyle uzun vadeli bir proje üzerinde çalışırken ve bir veya üç hatayı düzeltmek için ortasındaki dalları değiştirmek zorunda kalmamızdı.
Geçmişte, tüm şubeler için tek bir veritabanı kullanıyorduk ve yeni veritabanı değişiklikleriyle uyumlu olmayan bir şubeye geçiş yaptığımızda veritabanını yeniden oluşturmak zorunda kalıyorduk. Ve geri döndüğümüzde, yükseltmeleri tekrar çalıştırmak zorunda kalacağız.
mysqldump
Veritabanını farklı dallar için kopyalamaya çalıştık , ancak bekleme süresi çok uzundu (40-50 dakika) ve bu arada başka bir şey yapamadık.
Bu çözüm veri tabanı klonlama süresini kısaltır (kahve ve banyo molası uzun bir öğle yemeği yerine düşünün).
Ortak görevler ve zamanları
Uyumsuz veritabanı değişikliklerine sahip dallar arasında geçiş yapmak, tek bir veritabanında 50+ dakika sürer, ancak mysqldump
bu kurulumla veya bu kodla ilk kurulum süresinden sonra hiç zaman yoktur . Bu kod sadece ~ 5 kat daha hızlı olurmysqldump
.
Bazı yaygın görevler ve her bir yöntemle yaklaşık ne kadar sürecekleri aşağıda verilmiştir:
Veritabanı değişiklikleriyle özellik dalı oluşturun ve hemen birleştirin:
- Tek veritabanı: ~ 5 dakika
- İle klonlayın
mysqldump
: 50-60 dakika
- Bu kodla klonlayın: ~ 18 dakika
Veritabanı değişiklikleriyle özellik dalı oluşturma, master
hata düzeltmesi için geçiş yapma, özellik dalında düzenleme yapma ve birleştirme:
- Tek veritabanı: ~ 60 dakika
- İle klonlayın
mysqldump
: 50-60 dakika
- Bu kodla klonlayın: ~ 18 dakika
Veritabanı değişiklikleriyle özellik dalı oluşturun, özellik dalında master
önceden düzenleme yaparken 5 kez hata düzeltmesi yapın ve birleştirin:
- Tek veritabanı: ~ 4 saat, 40 dakika
- İle klonlayın
mysqldump
: 50-60 dakika
- Bu kodla klonlayın: ~ 18 dakika
Kod
Yukarıdaki her şeyi okuma ve anlamadığınız sürece bunu kullanmayın.
#!/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
Her şey yolunda giderse, şöyle bir şey görmelisiniz: