Fazendo backup de um banco de dados MySQL por meio de instantâneos do ZFS

12

Encontrei vários sites falando sobre fazer exatamente isso, mas estou perdendo alguns detalhes importantes. Os passos gerais são

  • Corre FLUSH TABLES WITH READ LOCK
  • Tire o instantâneo do ZFS
  • Corre UNLOCK TABLES

Várias fontes relatam que o InnoDB, que estou usando, na verdade não honra a FLUSH. O manual do usuário do MySQL observa que existe uma FLUSH TABLES...FOR EXPORTvariante para uso com o InnoDB, mas isso requer a especificação de cada tabela individualmente, em vez de fazer backup de todo o banco de dados. Eu preferiria evitar especificar cada tabela individualmente, porque há uma chance decente de que a lista de tabelas fique fora de sincronia com as tabelas que realmente existem.

O outro problema que tenho é que planejei fazer algo assim mysql -h"$HOST" -u"$USERNAME" -p"$PASSWORD" --execute="FLUSH TABLES WITH READ LOCK". No entanto, isso interrompe o bloqueio imediatamente após a saída da sessão. Isso faz sentido, mas também é bastante irritante, pois preciso segurar o bloqueio de leitura quando tiro minha captura instantânea.

Minha outra idéia é fazer um backup quente usando uma ferramenta como Percona XtraBackup e tirar instantâneos do backup, mas eu prefiro não pagar o custo de gravar todos os meus dados em um segundo local apenas para capturá-los.

Andy Shulman
fonte
Por que ter uma lista estática de tabelas? Você certamente pode gerar uma lista dinamicamente em tempo de execução.
EEAA
1
O banco de dados está em uma VM ou no bare metal? O armazenamento está na mesma máquina?
Michael Hampton
EEAA, bastante justo.
Andy Shulman
Michael, o banco de dados e a caixa ZFS são máquinas diferentes, mas nenhuma é virtualizada.
Andy Shulman
@ AndyShulman Eu acho que você deveria explicar o layout um pouco melhor. Isso não faz sentido.
precisa saber é

Respostas:

4

Se você usar o InnoDB apenas para todas as tabelas e definido innodb_flush_log_at_trx_commitcomo:

  • 1 (o conteúdo do buffer de log do InnoDB é gravado no arquivo de log em cada transação confirmada e o arquivo de log é liberado no disco) ou,
  • 2 (o conteúdo do buffer de log do InnoDB é gravado no arquivo de log após cada transação confirmada e o arquivo de log é liberado no disco aproximadamente uma vez por segundo),

então você não precisa de FLUSH TABLES antes de fazer o snapshot, basta executar o snapshot do ZFS diretamente. O InnoDB pode recuperar dados de logs de confirmação de transação sem perda de dados.

Ref: https://dev.mysql.com/doc/refman/5.5/en/innodb-parameters.html#sysvar_innodb_flush_log_at_trx_commit

Gea-Suan Lin
fonte
Com o dicionário de dados introduzido no MySQL 8, até as operações DDL (modificação de esquema) agora são atômicas. Antes disso, as operações DDL durante um instantâneo do sistema de arquivos podiam fornecer resultados parcialmente confirmados (ou seja, corrompidos).
bernie
13

Você precisa de um bloqueio completo do banco de dados para fazer backup de um ou mais bancos de dados de forma consistente.

O manual https://dev.mysql.com/doc/refman/5.5/en/backup-methods.html diz que FLUSH TABLES WITH READ LOCK está correto para os snapshots do ZFS especificamente.

Fazendo backups usando um instantâneo do sistema de arquivos

Se você estiver usando um sistema de arquivos Veritas, poderá fazer um backup como este:

  1. Em um programa cliente, execute FLUSH TABLES WITH READ LOCK.
  2. De outro shell, execute o vxfsinstantâneo de montagem .
  3. No primeiro cliente, execute UNLOCK TABLES.
  4. Copie arquivos do instantâneo.
  5. Desmonte o instantâneo.

Recursos semelhantes de captura instantânea podem estar disponíveis em outros sistemas de arquivos, como LVM ou ZFS.

É meio ridículo que eles deixem de fora o fato de que você precisa FLUSH TABLES table_a, table_b, table_c FOR EXPORTdo InnoDB a partir dessas instruções. Também é estúpido ter que especificar cada tabela assim. Mas, como diz o EEAA, você pode gerar uma lista de tabelas ao iniciar o backup com bastante facilidade.

Quanto à retenção do bloqueio, você deve manter a conexão db ativa ao executar o instantâneo

Geralmente eu usaria algo como Perl ou outra linguagem de programação que possa conectar, bloquear o banco de dados e, mantendo a conexão com o banco de dados, tire o instantâneo e desbloqueie e desconecte. Não é complexo. Eu apostaria que existem ferramentas por aí que já fazem isso, mas escrever uma é fácil.

Eu digo fácil, não complexo, etc. algumas vezes. Suponho que você tenha alguma programação básica ou boas habilidades de script.

Ryan Babchishin
fonte
Eu esperava manter um script tão conceitualmente simples no Bash, mas você está certo ao trocar de idioma, isso facilita muito. Eu posso estar lendo sua resposta errada, mas parece que você está dizendo que eu preciso executar ambos FLUSH TABLES WITH READ LOCKe depois FLUSH TABLES...FOR EXPORT, enquanto minha leitura do manual do MySQL diz que apenas uma deve ser necessária.
Andy Shulman
Desculpe, eu não estava claro. Estou apenas seguindo o manual e ele diz duas coisas diferentes. Eu estou supondo que você está correto e só precisa mais tarde. Mas todas as tabelas devem estar bloqueadas em um comando.
Ryan Babchishin 26/09/16
1
Como a documentação não é muito clara, o banco de dados inteiro precisa ser bloqueado e a conexão com o banco de dados deve ser mantida enquanto o instantâneo está sendo obtido, parece mais fácil desligar o banco de dados, fazer backup e reiniciar isto.
Andrew Henle
2
@ andrew suspiro ... eu entendo. Mas isso será lento, fará com que as conexões caiam / falhem e vi que os bancos de dados falham ao retornar corretamente (ruim para automação). Seria bom obter uma resposta definitiva do mysql / Oracle. Eles devem ter uma lista de endereçamento.
Ryan Babchishin
7

Eu rasguei e adaptei um script conceitualmente simples no Bash, que encontrei em outro post de Tobia sobre Server Fault . Deve chegar a cerca de 90% do caminho até lá.

mysql_locked=/var/run/mysql_locked

# flush & lock MySQL, touch mysql_locked, and wait until it is removed
mysql -hhost -uuser -ppassword -NB <<-EOF &
    flush tables with read lock;
    delimiter ;;
    system touch $mysql_locked
    system while test -e $mysql_locked; do sleep 1; done
    exit
EOF

# wait for the preceding command to touch mysql_locked
while ! test -e $mysql_locked; do sleep 1; done

# take a snapshot of the filesystem, while MySQL is being held locked
zfs snapshot zpool/$dataset@$(date +"%Y-%m-%d_%H:%M")

# unlock MySQL
rm -f $mysql_locked

Aqui, o mysqlcomando que você usa é executado em segundo plano e toca em um arquivo. Ele espera em segundo plano o arquivo desaparecer antes de sair e, assim, desbloquear as tabelas. Enquanto isso, o script principal espera até que o arquivo exista, cria o instantâneo e exclui o arquivo.

O arquivo apontado por $mysql_lockedprecisa estar acessível para ambas as máquinas, o que você deve poder fazer com bastante facilidade, pois elas podem acessar um conjunto de dados comum (embora possam usar caminhos diferentes, e você deve explicar isso).

Michael Hampton
fonte
Eu não sei scripts do MySQL, então isso pode ser uma idéia boba, mas você não poderia simplesmente fazer system zfs snapshot...dentro do script principal? Ou a captura instantânea precisa ser executada em um processo separado?
TripeHound 26/09/16
@Tripehound as duas coisas precisa acontecer em paralelo de alguma forma
Ryan Babchishin
@RyanBabchishin Acho que ele está certo, na verdade. O SYSTEMcomando executa as coisas localmente. Se eu rodar o cliente mysql na caixa do FreeBSD e executar LOCK; SYSTEM zfs snapshot; UNLOCK, parece que funcionaria.
Andy Shulman
@ Andy, eu apenas disse que eles precisam acontecer em paralelo. Não importa como você faz isso.
Ryan Babchishin
2

Você precisa de FLUSH TABLES WITH READ LOCK para myisam porque não está registrando em diário.

Você realmente não precisa de nada para o innodb, IMO, porque é diário. De qualquer forma, será consistente, apenas reverterá o diário automaticamente se algo estiver acontecendo no instante atômico em que você captura a foto.

Se você deseja consistência no nível do aplicativo, ele deve usar transações. Se seu aplicativo usar transações e innodb, qualquer instantâneo será consistente, solicitando o caminho até o nível do aplicativo automaticamente.

Jim Salter
fonte
2

Esta é minha solução, como criar um instantâneo do ZFS, mantendo o bloqueio:

mysql << EOF
    FLUSH TABLES WITH READ LOCK;
    system zfs snapshot data/db@snapname
    UNLOCK TABLES;
EOF
Petr Stastny
fonte