Le moyen le plus sûr d’exécuter mysqldump sur un système actif avec des lectures et écritures actives?


79

Je ne sais pas si cela est vrai, mais je me souviens de l'avoir lu si vous exécutez la commande suivante sous Linux

mysqldump -u username -p database_name > backup_db.sql

alors que des lectures et des écritures sont en cours dans une base de données, le vidage peut contenir des erreurs.

Existe-t-il des options particulières dans la commande mysqldumppour s’assurer que cela est effectué en toute sécurité sur un système actif? Les lectures / écritures sont désactivées pour nos utilisateurs pendant quelques secondes (la base de données <50 Mo).

Réponses:


84

Toutes les données sont InnoDB

Voici ce qui vous donnera un instantané exact des données:

mysqldump -uuser -ppass --single-transaction --routines --triggers --all-databases > backup_db.sql

--single-transactionproduit un point de contrôle qui permet au vidage de capturer toutes les données avant le point de contrôle lors de la réception des modifications entrantes. Ces modifications entrantes ne font pas partie du dump. Cela garantit le même moment pour toutes les tables.

--routines vide toutes les procédures stockées et les fonctions stockées

--triggers décharge tous les déclencheurs pour chaque table qui les contient

Toutes les données sont MyISAM ou un mélange d'InnoDB / MyISAM

Vous devrez imposer un verrou global en lecture, effectuer le mysqldump et libérer le verrou global.

mysql -uuser -ppass -Ae"FLUSH TABLES WITH READ LOCK; SELECT SLEEP(86400)" &
sleep 5
mysql -uuser -ppass -ANe"SHOW PROCESSLIST" | grep "SELECT SLEEP(86400)" > /tmp/proclist.txt
SLEEP_ID=`cat /tmp/proclist.txt | awk '{print $1}'`
echo "KILL ${SLEEP_ID};" > /tmp/kill_sleep.sql
mysqldump -uuser -ppass --single-transaction --routines --triggers --all-databases > backup_db.sql
mysql -uuser -ppass -A < /tmp/kill_sleep.sql

Essaie !!!

MISE À JOUR 2012-06-22 08:12 EDT

Depuis que vous avez <50 Mo de données totales, j'ai une autre option. Au lieu de lancer une commande SLEEP en arrière-plan pour conserver le verrou de lecture global pendant 86400 secondes (que 24 heures) juste pour obtenir l'ID de processus et le tuer à l'extérieur, essayons de définir un délai d'expiration de 5 secondes dans mysql plutôt que dans le système d'exploitation:

SLEEP_TIMEOUT=5
SQLSTMT="FLUSH TABLES WITH READ LOCK; SELECT SLEEP(${SLEEP_TIMEOUT})"
mysql -uuser -ppass -Ae"${SQLSTMT}" &
mysqldump -uuser -ppass --single-transaction --routines --triggers --all-databases > backup_db.sql

C'est une approche plus simple et plus propre pour les très petites bases de données.


1
5 secondes n'est que précaution. Vous pouvez peut essayer des valeurs plus basses.
RolandoMySQLDBA

1
Rolando - est ERROR 2013 (HY000) at line 1: Lost connection to MySQL server during queryun message d'erreur attendu?
user784637

1
Est-ce que toutes les données MySQL sont sorties dans le mysqldump?
RolandoMySQLDBA

1
Je ne suis pas sûr du message d'erreur. Ce n'est qu'une supposition, mais cela pourrait provenir du script d'une ligne qui tue l'appel de fonction SLEEP défini par l'utilisateur que j'ai mentionné dans le second script.
RolandoMySQLDBA

1
Essayez ma nouvelle suggestion et voyez si cela se passe bien. Espérons qu'il n'y aura pas de message d'erreur.
RolandoMySQLDBA

2
  • Pour les tables InnoDB, vous devez utiliser l' --single-transactionoption, comme indiqué dans une autre réponse.
  • Pour MyISAM, il y en a --lock-tables.

Voir la documentation officielle ici


1

Si vous souhaitez effectuer cette opération pour MyISAM ou des tables mélangées sans aucun temps d'arrêt, vous pouvez configurer une base de données esclave et prendre vos instantanés à partir de là. La configuration de la base de données esclave, malheureusement, entraîne l’exportation de la base de données dynamique, mais une fois qu’elle est en cours d’exécution, vous devriez pouvoir verrouiller ses tables et exporter à l’aide des méthodes décrites par d’autres. Lorsque cela se produit, il sera en retard sur le maître, mais n'empêchera pas le maître de mettre à jour ses tables et se mettra au courant dès que la sauvegarde sera terminée.


1

Voici comment je l'ai fait. Cela devrait fonctionner dans tous les cas puisqu'il utilise FLUSH TABLES WITH READ LOCK.

#!/bin/bash

DB=example
DUMP_FILE=export.sql

# Lock the database and sleep in background task
mysql -uroot -proot $DB -e "FLUSH TABLES WITH READ LOCK; DO SLEEP(3600);" &
sleep 3

# Export the database while it is locked
mysqldump -uroot -proot --opt $DB > $DUMP_FILE

# When finished, kill the previous background task to unlock
kill $! 2>/dev/null
wait $! 2>/dev/null

echo "Finished export, and unlocked !"

La sleepcommande shell sert uniquement à s'assurer que la tâche en arrière-plan exécutant la commande de verrouillage mysql est exécutée avant le démarrage de mysqldump. Vous pouvez le réduire à 1 seconde et tout devrait bien se passer. Augmentez-le à 30 secondes et essayez d'insérer une valeur dans n'importe quelle table à partir d'un autre client pendant ces 30 secondes.

L'utilisation de ce verrouillage manuel en arrière-plan présente deux avantages, au lieu d'utiliser les mysqldumpoptions --single-transactionet --lock-tables:

  1. Cela verrouille tout, si vous avez mélangé des tables MyISAM / InnoDB.
  2. Vous pouvez exécuter d'autres commandes en plus du mysqldumppendant la même période de verrouillage. Cela est utile, par exemple, lors de la configuration de la réplication sur un nœud maître, car vous devez obtenir la position du journal binaire avec SHOW MASTER STATUS;l'état exact du dump que vous avez créé (avant de déverrouiller la base de données) pour pouvoir créer un esclave de réplication.


0

Si vous avez une très grande table MYISAM et que vous devez vider la table sans verrou et éviter une charge de serveur élevée, vous pouvez utiliser le script suivant.

#!/bin/sh

my_user="user"
my_password="password"
my_db="vpn"
my_table="traffic"
my_step=100000

read -p "Dumping table ${my_db}.${my_table} to ${my_table}.sql?" yn
case $yn in
    [Yy]* ) break;;
    * ) echo "User cancel."; exit;;
esac

my_count=$(mysql $my_db -u $my_user -p$my_password -se "SELECT count(*) FROM $my_table")
my_count=$(($my_count + 0))

if [ ! $my_count ]
then
    echo "No records found"
    exit
fi

echo "Records in table ${my_db}.${my_table}: ${my_count}"

echo "" > $my_table.sql

max_progress=60

for (( limit=0; limit<=$my_count; limit+=$my_step )); do
    progress=$((max_progress * ( limit + my_step) / my_count))

    echo -ne "Dumping ["
    for ((i=0; i<$progress; i ++)); do
        echo -ne "#"
    done
    for ((; i<$max_progress; i ++)); do
        echo -ne "."
    done

    mysqldump -u $my_user -p$my_password --complete-insert --no-create-info --opt --where="1 limit $limit , $my_step" $my_db $my_table >> $my_table.sql
    echo "" >> $my_table.sql

    echo -ne "] $((100 * ( limit + my_step ) / my_count)) %"
    echo -ne "\r"

    sleep 1

done

echo -ne "\n"
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.