recuperar um único banco de dados mysql em um sistema replicado master-slave ocupado

10

Procurando uma estratégia ou ferramenta para lidar com a recuperação de um único banco de dados em um ponto no tempo em um sistema replicado ocupado.

Eu tenho 12 bancos de dados em execução em 2 servidores MySQL 5.0.77 na configuração replicada master-slave. Um despejo completo é obtido diariamente do escravo somente leitura, e há despejos SQL incrementais disponíveis, com esses backups externos e o status da replicação é monitorado.

Editar: As tabelas são uma mistura de InnoDB e myISAM, portanto, soluções específicas de mecanismo não estão disponíveis.

Portanto, dada uma falha completa do servidor mestre, posso interromper a replicação e promover o servidor escravo, também tenho a opção de reconstruir um novo servidor e configurar a partir do backup COMPLETO off-line e aplicar os diferenciais obtidos a cada hora do escravo.

No entanto, estou preocupado em como lidar com falhas parciais ou falhas de um único banco de dados. Eu posso pensar em dois cenários bastante prováveis;

  1. o banco de dados 7 (por exemplo) fica corrompido, continua a atender a algumas solicitações até que alguém note que está quebrado ou alerta dos arquivos de log ...
  2. Algumas consultas, como soltar banco de dados, soltar tabela, "atualizar onde ...", consulta tipo com um único banco de dados ou algum subconjunto nele.

No momento, tenho vários despejos COMPLETOS como arquivos FULL- $ DATE-all-database.sql.gz e diferenciais que podem ser aplicados aos despejos FULL como DIFF- $ DATE-all-database.sql.gz

Para restaurar o banco de dados 7 em algum momento, seria necessário um grep através dos arquivos FULL e DIFF e aplicação manual desse sql.

Como devo proceder para possibilitar a recuperação de um dos despejos DIFF anteriores no banco de dados mestre?

Preciso fazer backup em arquivos de banco de dados individuais, ou seja,

mysqldump --databases "database1" | gzip > database1.sql.gz
mysqldump --databases "database2" | gzip > database2.sql.gz
mysqldump --databases "database3" | gzip > database3.sql.gz

ao invés de..

mysqldump --master-data --lock--all-databases --all-databases | gzip > all-databases.sql.gz

Se eu for para arquivos mysqldump separados, o que acontece com o log binário de dados mestre e devo mesmo configurar --master-data para os despejos de recuperação do servidor mestre?

Tom H
fonte

Respostas:

7

Se todo o seu banco de dados usa apenas o InnoDB, tenho boas notícias.

Você deve despejar todo o banco de dados em paralelo de um escravo.

De fato, você pode forçar todos os bancos de dados no mesmo momento.

A primeira coisa a lembrar sobre um Escravo é que não é necessário ter o registro binário ativado, se não for um Mestre para outros Escravos.

Você não pode usar a --master-dataopção para despejos paralelos porque cada despejo terá uma posição diferente gravada na linha 22 de cada arquivo de despejo. É melhor gravar o último arquivo de log do mestre e posicionar o escravo executado usando SHOW SLAVE STATUS\G. Dessa forma, todos os bancos de dados têm a mesma posição point-in-time.

Você pode coletar todos os bancos de dados e criar scripts para o dump paralelo de todo o banco de dados.

DBLIST=/tmp/ListOfDatabasesToParallelDump.txt
BACKUP_BASE=/backups
BACKUP_DATE=`date +"%Y%m%d_%H%M%S"`
BACKUP_HOME=${BACKUP_BASE}/${BACKUP_DATE}
mkdir ${BACKUP_HOME}
cd ${BACKUP_HOME}

mysql -h... -u... -p... -e"STOP SLAVE;"
mysql -h... -u... -p... -e"SHOW SLAVE STATUS\G" > ${SSS}
LOGFIL=`cat ${SSS} | grep "Relay_Master_Log_File" | awk '{print $2}'`
LOGPOS=`cat ${SSS} | grep "Exec_Master_Log_Pos"   | awk '{print $2}'`
echo "Master was at ${LOGFIL} Position ${LOGPOS} for this Backup" > Master_Log_FilePos.txt

mysql -h... -u... -p... -AN -e"SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT IN ('information_schema','mysql','performance_schema')" > ${DBLIST}

for DB in `cat ${DBLIST}` 
do 
    mysqldump -h... -u... -p... --hex-blob --routines --triggers ${DB} | gzip > ${DB}.sql.gz & 
done 
wait 

mysql -h... -u... -p... -e"START SLAVE;"

Se houver simplesmente muitos bancos de dados, despeje-os 10 ou 20 por vez, da seguinte maneira:

DBLIST=/tmp/ListOfDatabasesToParallelDump.txt
SSS=/tmp/ShowSlaveStatusDisplay.txt
BACKUP_BASE=/backups
BACKUP_DATE=`date +"%Y%m%d_%H%M%S"`
BACKUP_HOME=${BACKUP_BASE}/${BACKUP_DATE}
mkdir ${BACKUP_HOME}
cd ${BACKUP_HOME}

mysql -h... -u... -p... -e"STOP SLAVE;"
mysql -h... -u... -p... -e"SHOW SLAVE STATUS\G" > ${SSS}
LOGFIL=`cat ${SSS} | grep "Relay_Master_Log_File" | awk '{print $2}'`
LOGPOS=`cat ${SSS} | grep "Exec_Master_Log_Pos"   | awk '{print $2}'`
echo "Master was at ${LOGFIL} Position ${LOGPOS} for this Backup" > Master_Log_FilePos.txt

mysql -h... -u... -p... -AN -e"SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT IN ('information_schema','mysql','performance_schema')" > ${DBLIST}

COMMIT_LIMIT=20
COMMIT_COUNT=0    
for DB in `cat ${DBLIST}` 
do 
    mysqldump -h... -u... -p... --hex-blob --routines --triggers ${DB} | gzip > ${DB}.sql.gz & 
    (( COMMIT_COUNT++ ))
    if [ ${COMMIT_COUNT} -eq ${COMMIT_LIMIT} ]
    then
        COMMIT_COUNT=0
        wait
    fi
done 
wait 
if [ ${COMMIT_COUNT} -gt 0 ]
then
    wait
fi

mysql -h... -u... -p... -e"START SLAVE;"

Se precisar recuperar uma única tabela, é possível paralelizar as tabelas de despejo 20 por vez em ordem de tamanho.

Tente o seguinte:

TBLIST=/tmp/ListOfTablesToParallelDump.txt
SSS=/tmp/ShowSlaveStatusDisplay.txt
BACKUP_BASE=/backups
BACKUP_DATE=`date +"%Y%m%d_%H%M%S"`
BACKUP_HOME=${BACKUP_BASE}/${BACKUP_DATE}
mkdir ${BACKUP_HOME}
cd ${BACKUP_HOME}

mysql -h... -u... -p... -e"STOP SLAVE;"
mysql -h... -u... -p... -e"SHOW SLAVE STATUS\G" > ${SSS}
LOGFIL=`cat ${SSS} | grep "Relay_Master_Log_File" | awk '{print $2}'`
LOGPOS=`cat ${SSS} | grep "Exec_Master_Log_Pos"   | awk '{print $2}'`
echo "Master was at ${LOGFIL} Position ${LOGPOS} for this Backup" > Master_Log_FilePos.txt

mysql -h... -u... -p... -AN -e"SELECT CONCAT(table_schema,'.',table_name) FROM information_schema.tables WHERE table_schema NOT IN ('information_schema','mysql','performance_schema') ORDER BY data_length" > ${DBLIST}

COMMIT_LIMIT=20
COMMIT_COUNT=0    
for DBTB in `cat ${TBLIST}` 
do
    DB=`echo "${DBTB}" | sed 's/\./ /g' | awk '{print $1}'`
    TB=`echo "${DBTB}" | sed 's/\./ /g' | awk '{print $2}'`
    DUMPFILE=$DB-{DB}-TBL-${TB}.sql.gz
    mysqldump -h... -u... -p... --hex-blob --routines --triggers ${DB} ${TB} | gzip >  ${DUMPFILE} & 
    (( COMMIT_COUNT++ ))
    if [ ${COMMIT_COUNT} -eq ${COMMIT_LIMIT} ]
    then
        COMMIT_COUNT=0
        wait
    fi
done 
wait 
if [ ${COMMIT_COUNT} -gt 0 ]
then
    wait
fi

mysql -h... -u... -p... -e"START SLAVE;"

Agora que você possui scripts para despejar bancos de dados ou tabelas individuais, pode carregar esses dados a seu critério. Se você precisar executar o SQL a partir dos logs binários no mestre, poderá usar mysqlbinloge dar a posição de data e hora e enviar o SQL para outros arquivos de texto. Você só precisa executar a devida diligência para encontrar a quantidade de dados necessária a partir de qualquer registro de data e hora que os registros binários possuam. Lembre-se de que o registro de data e hora de cada log binário no sistema operacional representa a última vez em que foi gravado.

RolandoMySQLDBA
fonte
respostas brilhantes thanx. Eu acho que ter um escravo somente leitura no xfs me oferece muitas opções, e seus scripts realmente ajudaram.
Tom H
no cenário em que preciso recuperar uma tabela enorme para o mestre a partir de um backup do escravo. Eu só tenho que reconstruir a tabela no mestre e todas as alterações são replicadas para o escravo, mesmo que sejam 20 GB de dados? O processo seria 1) desabilitar chaves, 2) soltar a tabela no mestre e no escravo 3) restaurar a tabela no mestre e 4) ativar as chaves --- e fazer com que o mestre replique todos os 20 GB até o escravo?
Tom H
Se esses bancos de dados NÃO forem innodb, ainda posso despejá-los em paralelo?
Tom H
Sim, se você 1) agendar o tempo de inatividade, 2) executar service mysql restart --skip-networking, 3) executar o dump paralelo, 4) executar service mysql restart. Em seguida, recarregue as tabelas necessárias.
RolandoMySQLDBA 20/01/12
presumivelmente, se o objetivo da reinicialização era impedir que as conexões de rede fossem gravadas no banco de dados, eu poderia obter o mesmo efeito usando iptables i.e. iptables -I INPUT -p tcp --dport 3306 -j DROPeth0 e lo
Tom H