¿La forma más segura de realizar mysqldump en un sistema en vivo con lecturas y escrituras activas?

78

No estoy seguro de si esto es cierto, pero recuerdo haber leído si ejecuta el siguiente comando en Linux

mysqldump -u username -p database_name > backup_db.sql

mientras se realizan lecturas y escrituras en una base de datos, el volcado puede contener errores.

¿Hay opciones particulares en el comando mysqldumppara asegurarse de que esto se haga de forma segura en un sistema en vivo? Estoy de acuerdo con que las lecturas / escrituras se deshabiliten para nuestros usuarios durante unos segundos (la base de datos <50 MB)

usuario784637
fuente

Respuestas:

82

Todos los datos son InnoDB

Esto es lo que le dará una instantánea exacta de los datos en un momento dado:

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

--single-transactionproduce un punto de control que permite que el volcado capture todos los datos antes del punto de control mientras recibe los cambios entrantes. Esos cambios entrantes no se convierten en parte del volcado. Eso garantiza el mismo punto en el tiempo para todas las tablas.

--routines vuelca todos los procedimientos almacenados y funciones almacenadas

--triggers vuelca todos los disparadores para cada tabla que los tiene

Todos los datos son MyISAM o Mix de InnoDB / MyISAM

Tendrá que imponer un bloqueo de lectura global, ejecutar mysqldump y liberar el bloqueo 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

Darle una oportunidad !!!

ACTUALIZACIÓN 2012-06-22 08:12 EDT

Como tiene <50 MB de datos totales, tengo otra opción. En lugar de lanzar un comando SLEEP en segundo plano para mantener el bloqueo de lectura global durante 86400 segundos (esas 24 horas) solo para obtener la identificación del proceso y matar afuera, intentemos configurar un tiempo de espera de 5 segundos en mysql en lugar de en el sistema operativo:

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

Este es un enfoque más limpio y simple para bases de datos muy pequeñas.

RolandoMySQLDBA
fuente
1
5 segundos es solo precaución. Puedes probar valores más bajos.
RolandoMySQLDBA
1
Rolando: ¿es ERROR 2013 (HY000) at line 1: Lost connection to MySQL server during queryun mensaje de error esperado?
user784637
1
¿Todos los datos de MySQL salieron en el mysqldump?
RolandoMySQLDBA
1
No estoy seguro sobre el mensaje de error. Esto es solo una suposición, pero podría haber venido del script de una línea que mata la llamada a la función SLEEP definida por el usuario que mencioné en el segundo script.
RolandoMySQLDBA
1
Pruebe mi nueva sugerencia y vea si funciona bien. Con suerte, no habrá mensaje de error.
RolandoMySQLDBA
2
  • Para las tablas InnoDB, debe usar la --single-transactionopción, como se menciona en otra respuesta.
  • Para MyISAM existe --lock-tables.

Vea la documentación oficial aquí.

pesco
fuente
1

Si desea hacer esto para MyISAM o tablas mixtas sin ningún tiempo de inactividad por el bloqueo de las tablas, puede configurar una base de datos esclava y tomar sus instantáneas desde allí. La configuración de la base de datos esclava, desafortunadamente, causa un tiempo de inactividad para exportar la base de datos en vivo, pero una vez que se está ejecutando, debería poder bloquear sus tablas y exportar utilizando los métodos que otros han descrito. Cuando esto sucede, se quedará atrás del maestro, pero no impedirá que el maestro actualice sus tablas y se pondrá al día tan pronto como se complete el respaldo.

Talik Eichinger
fuente
1

Así es como lo hice. Debería funcionar en todos los casos, ya que utiliza 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 !"

El sleepcomando shell es solo para asegurarse de que la tarea en segundo plano que ejecuta el comando de bloqueo mysql se ejecute antes de que se inicie mysqldump. Podría reducirlo a 1 segundo y aún debería estar bien. Aumente a 30 segundos e intente insertar valores en cualquier tabla de otro cliente durante esos 30 segundos, verá que está bloqueado.

Hay 2 ventajas al usar este bloqueo manual de fondo, en lugar de usar las mysqldumpopciones --single-transactiony --lock-tables:

  1. Esto bloquea todo, si ha mezclado tablas MyISAM / InnoDB.
  2. Puede ejecutar otros comandos además del mysqldumpdurante el mismo período de bloqueo. Es útil, por ejemplo, al configurar la replicación en un nodo maestro, porque necesita obtener la posición del registro binario SHOW MASTER STATUS;en el estado exacto del volcado que creó (antes de desbloquear la base de datos), para poder crear una replicación esclava.
Nicomak
fuente
1

La sugerencia de la documentación oficial de mysql es que debe tener una base de datos maestra "M1" y una base de datos esclava "S1" que se describe en "Escenario 2: Copia de seguridad con un esclavo de solo lectura" Copia de seguridad de un maestro o esclavo al hacerlo Solo lectura

Debe configurar la base de datos esclava de solo lectura y realizar

Victor Hugo Arango A.
fuente
0

Si tiene una tabla MYISAM muy grande y necesita volcar la tabla sin bloqueo y evitar una carga elevada del servidor, puede usar el siguiente script.

#!/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"
vadim_hr
fuente