← Voltar ao hub MariaDB
MariaDB 04 de novembro de 2026 11 min de leitura

Backup no MariaDB sem parar o banco: mariadb-backup, incremental e restauração até o minuto exato

Backup no MariaDB sem parar o banco: mariadb-backup, incremental e restauração até o minuto exato

O dump funciona muito bem até o dia em que você precisa restaurar 300 GB com a diretoria olhando por cima do seu ombro.

Resumo para quem tem pressa: o MariaDB traz de graça uma ferramenta de backup físico a quente, o mariadb-backup. Ela copia os arquivos do banco com ele rodando, sem desligar o servidor, mas com bloqueios em determinadas etapas, e restaura muito mais rápido do que um dump SQL. Combinando um backup completo semanal, incrementais diários e o binlog, você consegue voltar o banco para qualquer minuto, por exemplo um segundo antes daquele DELETE sem WHERE. Neste artigo eu mostro os comandos, um script pronto para agendar e, o mais importante, como testar se o restore funciona.

Dump lógico x backup físico: qual é a diferença

Muita gente faz backup assim e dorme tranquila:

mariadb-dump --single-transaction --all-databases > backup.sql

Para bancos pequenos, é perfeito. O problema aparece com o crescimento. O dump gera comandos SQL, e restaurar significa reexecutar cada INSERT e reconstruir cada índice. Em bases de centenas de GB, isso leva horas, às vezes um dia inteiro.

O mariadb-backup faz outra coisa: copia os arquivos de dados do InnoDB enquanto o banco está no ar e registra tudo o que mudou durante a cópia, para deixar o resultado consistente. Restaurar é, basicamente, devolver os arquivos para o lugar.

mariadb-dump (lógico) mariadb-backup (físico)
Banco precisa parar? Não; consistência com --single-transaction exige tabelas transacionais e cuidado com DDL Não
Velocidade do backup Lenta em bases grandes Rápida, limitada pelo disco
Velocidade do restore Muito lenta (reexecuta SQL e recria índices) Rápida (copia arquivos)
Backup incremental Não Sim
Restaurar só uma tabela Fácil Possível, porém mais trabalhoso
Portável para outra versão ou banco Sim Não é portável como SQL: exige compatibilidade de versão e formato físico

A regra prática que uso nos clientes: dump para portabilidade e cópias pontuais, mariadb-backup para a recuperação de desastre. Os dois convivem bem.

Antes de tudo: um usuário só para backup

Não use root em script agendado. Crie um usuário com o mínimo necessário:

CREATE USER 'backup'@'localhost' IDENTIFIED BY 'troque-esta-senha';

-- Privilégios mínimos para o mariadb-backup (10.5 em diante)
GRANT RELOAD, PROCESS, LOCK TABLES, BINLOG MONITOR
  ON *.* TO 'backup'@'localhost';

-- Só se for fazer backup a partir de uma réplica (--slave-info)
GRANT REPLICA MONITOR ON *.* TO 'backup'@'localhost';

E guarde a senha num arquivo protegido, não na linha de comando (que aparece no ps para qualquer usuário do servidor):

# /etc/mysql/backup.cnf  (chmod 600, dono root)
[client]
user     = backup
password = troque-esta-senha

Atenção: use o mariadb-backup da mesma versão do servidor. Ele vem no pacote mariadb-backup da sua distribuição ou do repositório oficial. Uma versão diferente pode não entender o formato dos arquivos.

Backup completo

mariadb-backup --defaults-extra-file=/etc/mysql/backup.cnf \
  --backup \
  --target-dir=/backup/mariadb/full \
  --parallel=4

O --parallel copia vários arquivos ao mesmo tempo e pode ajudar conforme o armazenamento e a carga. Backup a quente não significa impacto zero: há consumo de I/O e bloqueios em determinadas etapas. O BACKUP STAGE procura reduzir esse impacto, mas pode bloquear DDL, escritas em tabelas não transacionais e commits. Transações e DDL em andamento também podem fazer o backup esperar; não dá para prometer “poucos instantes” em qualquer ambiente. Confira o comportamento da sua versão de manutenção na documentação oficial.

Dentro do diretório, além dos dados, ficam arquivos de controle. A partir do 11.1 eles têm o prefixo mariadb_backup_ (antes era xtrabackup_, herança da origem da ferramenta):

  • mariadb_backup_checkpoints: tipo de backup e posição no log. É o que permite os incrementais.
  • mariadb_backup_binlog_info: a posição exata do binlog no momento do backup. Guarde essa informação, porque vamos precisar dela na recuperação até o minuto.

Preparar: o passo que todo mundo esquece

Logo após a cópia, os arquivos ainda não estão consistentes, porque transações aconteceram durante o backup. O passo --prepare aplica o log e deixa tudo pronto para uso:

mariadb-backup --prepare --target-dir=/backup/mariadb/full

Ele não precisa de conexão com o banco e pode rodar em outra máquina. Já vi empresa com meses de backups "feitos" que nunca tinham passado pelo prepare e só descobriu isso na hora do desastre. Não seja essa empresa.

Restaurar

O restore exige o servidor parado e o diretório de dados vazio:

systemctl stop mariadb

# Guarde o diretório atual em vez de apagar (você vai me agradecer)
mv /var/lib/mysql /var/lib/mysql.antes-do-restore
mkdir /var/lib/mysql

# Copia os arquivos preparados de volta
mariadb-backup --copy-back --target-dir=/backup/mariadb/full

# Os arquivos precisam pertencer ao usuário do serviço
chown -R mysql:mysql /var/lib/mysql

systemctl start mariadb

Se o disco estiver apertado, --move-back move em vez de copiar, mas aí o backup deixa de existir naquele local. Prefira --copy-back.

Backups incrementais: só o que mudou

Fazer backup completo de 500 GB todo dia gasta disco e tempo. O incremental copia apenas as páginas alteradas desde o backup anterior:

# Domingo: completo
mariadb-backup --defaults-extra-file=/etc/mysql/backup.cnf \
  --backup --target-dir=/backup/mariadb/full

# Segunda: incremental em relação ao completo
mariadb-backup --defaults-extra-file=/etc/mysql/backup.cnf \
  --backup --target-dir=/backup/mariadb/inc1 \
  --incremental-basedir=/backup/mariadb/full

# Terça: incremental em relação ao de segunda
mariadb-backup --defaults-extra-file=/etc/mysql/backup.cnf \
  --backup --target-dir=/backup/mariadb/inc2 \
  --incremental-basedir=/backup/mariadb/inc1

Na hora de restaurar, prepare uma cópia do completo e aplique os incrementais em ordem sobre essa mesma cópia. Confira os checkpoints: o from_lsn de cada incremental precisa corresponder ao to_lsn da etapa anterior. Crie /restore antes da cópia e garanta que /restore/full ainda não exista:

# Trabalhe sempre numa CÓPIA: o prepare altera o diretório
mkdir -p /restore
test ! -e /restore/full || {
  echo "Destino /restore/full já existe; interrompendo." >&2
  exit 1
}
cp -a /backup/mariadb/full /restore/full

mariadb-backup --prepare --target-dir=/restore/full
mariadb-backup --prepare --target-dir=/restore/full \
  --incremental-dir=/backup/mariadb/inc1
mariadb-backup --prepare --target-dir=/restore/full \
  --incremental-dir=/backup/mariadb/inc2

# Depois disso, o restore é igual ao do backup completo

Atenção: pular um incremental ou aplicar fora de ordem invalida a cadeia. Se o inc1 se perder, o inc2 não serve para nada. É por isso que o completo semanal é importante: ele limita o tamanho da corrente.

Restauração até o minuto exato (point-in-time recovery)

O cenário clássico: às 14h37, alguém roda um UPDATE clientes SET ativo = 0 sem WHERE. O último backup é das 2h da manhã. Voltar para as 2h significa perder 12 horas de vendas.

Com o binlog ligado, você restaura o backup e depois reaplica tudo o que aconteceu entre as 2h e as 14h36. O binlog precisa estar ativo antes do incidente:

[mariadb]
log_bin          = /var/lib/mysql/mariadb-bin
binlog_format    = ROW
expire_logs_days = 8      # mantenha mais dias do que o intervalo entre completos

O passo a passo:

# 1. Restaure o backup (completo + incrementais) como mostrado acima

# 2. Veja onde o binlog estava no momento do backup
cat /restore/full/mariadb_backup_binlog_info
# Antes do 11.1, use: cat /restore/full/xtrabackup_binlog_info
# mariadb-bin.000231    48213    0-1-99812

# 3. Reaplique do ponto do backup até um instante antes do erro
mariadb-binlog --start-position=48213 \
  --stop-datetime="2026-11-03 14:36:59" \
  /binlogs/mariadb-bin.000231 /binlogs/mariadb-bin.000232 \
  | mariadb -u root -p

Atenção: copie os arquivos de binlog para fora do servidor antes de restaurar, porque o diretório de dados será substituído. E repare que o último comando usa um usuário administrativo: o usuário backup não tem permissão para escrever nas tabelas, e é assim que deve ser.

Antes de reaplicar, gere um arquivo e inspecione os eventos. Com binlog_format=ROW, um grep pelo SQL original não é suficiente: use mariadb-binlog --base64-output=DECODE-ROWS -vv para uma cópia de inspeção (não para replay). Confirme os limites das transações e prefira posições exatas de eventos para excluir a transação problemática; --stop-datetime depende do fuso horário e não garante precisão de negócio “até o minuto”. Mantenha a aplicação sem escrita durante o replay e use todos os binlogs necessários, sem lacunas. As coordenadas registradas só têm consistência garantida com o backup quando ele não usa --no-lock.

E se o estrago for em poucas tabelas e você quiser evitar esse tipo de acidente no futuro, vale conhecer as tabelas temporais. Com elas, o próprio MariaDB guarda o histórico das linhas e você consulta "como estava às 14h30" com um SELECT.

Compactar e tirar do servidor

Backup no mesmo disco do banco não é backup: se o disco morrer, vão os dois. O mariadb-backup pode gerar um fluxo único, que você compacta e envia direto para outra máquina:

# Backup compactado direto para outro servidor, sem arquivo intermediário
mariadb-backup --defaults-extra-file=/etc/mysql/backup.cnf \
  --backup --stream=xbstream --parallel=4 \
  | gzip \
  | ssh backup@servidor-remoto "cat > /backups/mariadb-$(date +%F).xb.gz"

# Para restaurar: descompactar e extrair, depois --prepare normalmente
mkdir /restore/full
gunzip -c mariadb-2026-11-03.xb.gz | mbstream -x -C /restore/full

Siga a regra 3-2-1: três cópias, em dois tipos de mídia, uma delas fora do local (outro datacenter ou um bucket em nuvem).

Rode o backup na réplica, não no primário

Se você tem replicação, faça o backup a partir da réplica. O primário fica livre para a aplicação:

mariadb-backup --defaults-extra-file=/etc/mysql/backup.cnf \
  --backup --target-dir=/backup/mariadb/full \
  --slave-info --safe-slave-backup

O --slave-info registra a posição do primário, o que permite usar o backup para criar uma réplica nova. O --safe-slave-backup coordena a parada da thread SQL da réplica e espera não haver tabelas temporárias abertas, podendo repetir esse processo até atingir o limite de espera. Não é uma pausa garantidamente restrita aos instantes finais. Se você ainda não tem réplica, o artigo sobre o Galera Cluster mostra uma opção de alta disponibilidade que já vem na caixa.

Script pronto para agendar

Este exemplo faz um backup completo no primeiro disparo de cada semana e incrementais nos demais. Uma trava evita execuções simultâneas e o diretório só recebe o nome definitivo depois de a ferramenta terminar com sucesso. A retenção automática fica fora do exemplo: nunca remova um completo separado dos incrementais que dependem dele.

#!/usr/bin/env bash
# /usr/local/bin/backup-mariadb.sh
set -euo pipefail
export LC_ALL=C
umask 077

BASE=/backup/mariadb
CRED=--defaults-extra-file=/etc/mysql/backup.cnf
mkdir -p "$BASE"
exec 9>"$BASE/.backup.lock"
flock -n 9 || { echo "Backup já está em execução." >&2; exit 1; }

SEMANA="$BASE/$(date +%G-%V)"
mkdir -p "$SEMANA"
PENDENTE=$(mktemp -d "$SEMANA/.pending-XXXXXXXX")
trap 'echo "Falha: confira o diretório parcial $PENDENTE; ele não será usado como base." >&2' ERR

if [[ ! -d "$SEMANA/full" ]]; then
  mariadb-backup "$CRED" --backup --target-dir="$PENDENTE" --parallel=4
  mv -T -- "$PENDENTE" "$SEMANA/full"
else
  shopt -s nullglob
  incrementais=( "$SEMANA"/inc-* )
  shopt -u nullglob
  ULTIMO="$SEMANA/full"
  for candidato in "${incrementais[@]}"; do
    [[ -d "$candidato" ]] || continue
    if [[ "$ULTIMO" == "$SEMANA/full" || "$candidato" > "$ULTIMO" ]]; then
      ULTIMO="$candidato"
    fi
  done
  DESTINO="$SEMANA/inc-$(date +%Y%m%d-%H%M%S)-$"
  mariadb-backup "$CRED" --backup --target-dir="$PENDENTE" \
    --incremental-basedir="$ULTIMO" --parallel=4
  mv -T -- "$PENDENTE" "$DESTINO"
fi

# Retenção: remova somente cadeias semanais inteiras e concluídas,
# após validar cópias externas e os binlogs exigidos pela sua política.
# A data de modificação do diretório não representa a idade da cadeia.

Agende no cron do root, por exemplo às 2h:

0 2 * * * /usr/local/bin/backup-mariadb.sh >> /var/log/backup-mariadb.log 2>&1

O exemplo pressupõe Linux com Bash, flock e GNU mv. Um diretório definitivo indica apenas que o comando terminou: isso não substitui preparar e restaurar o backup em teste. Resultados parciais ficam com prefixo .pending- e exigem inspeção.

Adapte o script para enviar o resultado para fora do servidor e para avisar quando falhar, seja por e-mail, Slack ou o seu monitoramento. Um backup que falha em silêncio é pior do que não ter backup, porque dá falsa segurança.

Backup que nunca foi restaurado é só uma esperança

Este é o ponto mais importante do artigo inteiro. Uma vez por mês, no mínimo:

  • Restaure o último backup (completo + incrementais) em um servidor de teste.
  • Suba o MariaDB e confira o log de erro.
  • Valide os dados: contagem de linhas das tabelas principais e um CHECK TABLE nas mais críticas.
  • Cronometre. O tempo de restore é o seu RTO real, aquele que você vai responder quando perguntarem "em quanto tempo voltamos?".
  • Ensaie o point-in-time pelo menos uma vez, com um erro simulado.

O que isso significa para você

  • Base pequena e pouco crítica: mariadb-dump --single-transaction diário, fora do servidor, e restore testado.
  • Base que cresce ou que não pode ficar fora do ar: mariadb-backup com completo semanal, incrementais diários, binlog ligado e cópia fora do local.
  • Qualquer cenário: teste o restore. Todo mês.

Tudo isso é nativo e gratuito no MariaDB Community, sem licença extra e sem ferramenta de terceiros. É um daqueles recursos que só aparecem de verdade no pior dia do ano, e nesse dia fazem toda a diferença.

E antes de criar índice novo ou mexer em configuração para resolver lentidão, garanta que este backup está em dia. Falo da parte de desempenho no artigo MariaDB lento? Como achar e corrigir as consultas que estão derrubando seu sistema.


Leia também

Todos os artigos sobre MariaDB →

Referências oficiais

Nota técnica: os exemplos foram revisados com a documentação, mas não executados em um servidor MariaDB nesta publicação. Valide versão, caminhos, privilégios e comportamento em laboratório antes de usar em produção. As capas são ilustrações fotográficas geradas por IA, não resultados de testes.

Continue essa conversa

Quer trocar experiências sobre MariaDB? Entre para a Comunidade MariaDB Brasil.

Me acompanhe no LinkedIn e no Instagram. Para fortalecer sua base em SQL, conheça meu livro Aprendendo MySQL. Conheça também a HTI Tecnologia.