A maneira mais segura de executar o mysqldump em um sistema ativo com leituras e gravações ativas?

78

Não tenho certeza se isso é verdade, mas lembro de ler se você executar o seguinte comando no linux

mysqldump -u username -p database_name > backup_db.sql

enquanto leituras e gravações estiverem sendo feitas em um banco de dados, o despejo pode conter erros.

Existem opções específicas no comando mysqldumppara garantir que isso seja feito com segurança em um sistema ativo? Eu estou bem com as leituras / gravações sendo desativadas para nossos usuários por alguns segundos (o banco de dados <50MB)

user784637
fonte

Respostas:

82

Todos os dados são InnoDB

É isso que lhe dará uma captura instantânea exata dos dados no momento:

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

--single-transactionproduz um ponto de verificação que permite ao despejo capturar todos os dados anteriores ao ponto de verificação enquanto recebe as alterações recebidas. Essas alterações recebidas não se tornam parte do despejo. Isso garante o mesmo momento para todas as tabelas.

--routines despeja todos os procedimentos armazenados e funções armazenadas

--triggers despeja todos os gatilhos para cada tabela que os possui

Todos os dados são MyISAM ou mix de InnoDB / MyISAM

Você precisará impor um bloqueio de leitura global, executar o mysqldump e liberar o bloqueio 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

De uma chance !!!

UPDATE 2012-06-22 08:12 EDT

Como você tem <50 MB do total de dados, tenho outra opção. Em vez de lançar um comando SLEEP em segundo plano para manter o bloqueio de leitura global por 86400 s (24 horas) apenas para obter o ID do processo e eliminar fora, vamos tentar definir um tempo limite de 5 segundos no mysql e não no SO:

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

Essa é uma abordagem mais limpa e simples para bancos de dados muito pequenos.

RolandoMySQLDBA
fonte
1
5 segundos é apenas uma precaução. Você pode tentar valores mais baixos.
RolandoMySQLDBA
1
Rolando - é ERROR 2013 (HY000) at line 1: Lost connection to MySQL server during queryuma mensagem de erro esperada?
user784637
1
Todos os dados do MySQL foram lançados no mysqldump?
RolandoMySQLDBA
1
Não tenho certeza da mensagem de erro. Isso é apenas um palpite, mas pode ter vindo do script de uma linha que mata a chamada de função SLEEP definida pelo usuário que mencionei no segundo script.
RolandoMySQLDBA
1
Tente minha nova sugestão e veja se vai bem. Felizmente, não haverá mensagem de erro.
RolandoMySQLDBA
2
  • Para tabelas do InnoDB, você deve usar a --single-transactionopção, conforme mencionado em outra resposta.
  • Para o MyISAM existe --lock-tables.

Veja a documentação oficial aqui

pesco
fonte
1

Se você quiser fazer isso no MyISAM ou em tabelas mistas sem tempo de inatividade para bloquear as tabelas, poderá configurar um banco de dados escravo e tirar suas capturas instantâneas a partir daí. A configuração do banco de dados escravo, infelizmente, causa algum tempo de inatividade para exportar o banco de dados ativo, mas quando estiver em execução, você poderá bloquear suas tabelas e exportar usando os métodos descritos por outros. Quando isso está acontecendo, fica para trás do mestre, mas não impede que o mestre atualize suas tabelas e recupera assim que o backup é concluído.

Talik Eichinger
fonte
1

Aqui está como eu fiz isso. Deve funcionar em todos os casos, uma vez que usa 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 !"

O sleepcomando shell é apenas para garantir que a tarefa em segundo plano executando o comando de bloqueio do mysql seja executada antes do início do mysqldump. Você pode reduzi-lo para 1 segundo e ele ainda deve estar bem. Aumente para 30 segundos e tente inserir valores em qualquer tabela de outro cliente durante os 30 segundos em que você verá que está bloqueado.

Há duas vantagens em usar esse bloqueio manual em segundo plano, em vez de usar as mysqldumpopções --single-transactione --lock-tables:

  1. Isso bloqueia tudo, se você misturou tabelas MyISAM / InnoDB.
  2. Você pode executar outros comandos além do mysqldumpdurante o mesmo período de bloqueio. É útil, por exemplo, ao configurar a replicação em um nó mestre, porque você precisa obter a posição do log binário SHOW MASTER STATUS;no estado exato do dump que você criou (antes de desbloquear o banco de dados), para poder criar um escravo de replicação.
Nicomak
fonte
1

A sugestão da documentação oficial do mysql é que você deve ter um banco de dados "M1" mestre e um banco de dados "S1" escravo, descrito em "Cenário 2: Backup com um escravo somente leitura" , fazendo backup de um mestre ou escravo, tornando-o Somente leitura

Você deve definir o banco de dados escravo somente leitura e executar

Victor Hugo Arango A.
fonte
0

se você tiver uma tabela MYISAM muito grande e precisar despejar a tabela sem bloqueio e evitar alta carga do servidor, poderá usar o seguinte 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
fonte