Estratégias de backup de banco de dados de produção que realmente funcionam: MySQL e PostgreSQL
Estratégias comprovadas de backup e recuperação para MySQL e PostgreSQL em produção
A perda de dados de produção é o tipo de incidente que encerra carreiras, fecha empresas e chega à primeira página do Hacker News pelos motivos errados. No entanto, a maioria das equipes trata os backups de banco de dados como uma reflexão tardia – um cron job que alguém configurou há dois anos e que ninguém verificou desde então. Este artigo apresenta estratégias de backup testadas em batalha para MySQL e PostgreSQL que foram comprovadas em ambientes de produção que vão desde clusters k3s de nó único até implantações em nuvem multirregionais que processam milhões de transações diariamente.
Cobriremos todo o espectro: métodos de backup lógico e físico, recuperação pontual, agendamento automatizado, alvos de armazenamento em nuvem, criptografia, verificação, abordagens nativas do Kubernetes e o planejamento de recuperação de desastres que une tudo isso. Cada recomendação vem com configuração e código concretos que você pode adaptar ao seu ambiente.
Compreendendo os tipos de backup
Antes de mergulhar em ferramentas específicas, você precisa de um modelo mental claro das quatro estratégias fundamentais de backup e de como elas interagem com os objetivos de recuperação. Cada estratégia faz uma compensação diferente entre velocidade de backup, consumo de armazenamento e tempo de recuperação.
Um backup completo doOcaptura todo o banco de dados em um único momento. É o mais simples de raciocinar e o mais rápido de restaurar, mas também o mais caro em termos de armazenamento e o mais lento de criar. Um backup incrementalOcaptura apenas os dados que foram alterados desde o último backup de qualquer tipo. É rápido de criar e compactar no armazenamento, mas a restauração requer a repetição de toda a cadeia: o último backup completo mais todos os incrementais subsequentes. Um backup diferencialOcaptura tudo o que mudou desde o último backup completo. Ele ocupa um meio-termo – maior que um incremental, mas mais simples de restaurar porque você só precisa do último completo mais o último diferencial. Por fim, o arquivamento contínuo(arquivamento WAL no PostgreSQL, streaming de log binário no MySQL) captura cada transação individual conforme ela acontece, permitindo a recuperação pontual a qualquer momento entre os backups.
A estratégia ideal para a maioria dos sistemas de produção é uma combinação: backups completos semanais, incrementais ou diferenciais diários e arquivamento WAL/binlog contínuo. Isso oferece recuperação rápida de backups completos recentes e a capacidade de recuperação a qualquer momento quando necessário.
Métodos de backupMySQL
MySQL oferece diversas ferramentas de backup, cada uma adequada para diferentes tamanhos de banco de dados e requisitos de recuperação. A escolha certa depende do volume de dados, da janela de backup aceitável e dos objetivos de RTO/RPO.
mysqldump — O Backup Lógico Universal
mysqldumpproduz instruções SQL que recriam o esquema e os dados. Ele funciona em todas as versões e mecanismos de armazenamento do MySQL, tornando-o um substituto universal. No entanto, ele bloqueia tabelas durante o dump (a menos que use--single-transactioncom InnoDB) e a velocidade de restauração diminui significativamente para bancos de dados além de 50–100 GB porque reproduz instruções INSERT individuais.
# Full logical backup with consistent snapshot for InnoDB
mysqldump --single-transaction --routines --triggers --events \
--set-gtid-purged=ON --all-databases \
| gzip > /backups/mysql-full-$(date +%Y%m%d-%H%M%S).sql.gz
# Single database backup with compression
mysqldump --single-transaction --routines --triggers \
--databases production_db \
| pigz -p4 > /backups/production_db-$(date +%Y%m%d).sql.gz
# Schema-only backup for migration planning
mysqldump --no-data --routines --triggers --events \
--all-databases > /backups/schema-only-$(date +%Y%m%d).sqlmysqlpump — Backup lógico paralelo
mysqlpumpmelhora omysqldumpcom despejo de tabela paralelo e compactação integrada. Pode reduzir significativamente o tempo de backup para bancos de dados com muitas tabelas independentes.
# Parallel logical backup with 4 threads and zstd compression
mysqlpump --default-parallelism=4 --compress-output=ZSTD \
--include-databases=production_db,analytics_db \
--set-gtid-purged=ON \
> /backups/mysql-pump-$(date +%Y%m%d).sql.zstPercona XtraBackup — Backups físicos para InnoDB
Para bancos de dados maiores que 50 GB, os backups lógicos tornam-se impraticáveis — tanto a janela de backup quanto o tempo de restauração crescem linearmente com o tamanho dos dados. O Percona XtraBackup faz cópias de nível físico dos arquivos de dados do InnoDB sem bloquear o banco de dados, tornando-o adequado para implantações de vários terabytes.
# Full physical backup with streaming to compressed archive
xtrabackup --backup --target-dir=/backups/full-$(date +%Y%m%d) \
--user=backup_user --password=secure_pass \
--parallel=4 --compress --compress-threads=4
# Incremental backup based on last full
xtrabackup --backup --target-dir=/backups/incr-$(date +%Y%m%d) \
--incremental-basedir=/backups/full-20260412 \
--user=backup_user --password=secure_pass \
--parallel=4
# Stream full backup directly to S3 via xbstream
xtrabackup --backup --stream=xbstream --compress \
--user=backup_user --password=secure_pass | \
aws s3 cp - s3://db-backups/mysql/full-$(date +%Y%m%d).xbstream
# Prepare for restore (apply redo log)
xtrabackup --prepare --target-dir=/backups/full-20260412
# Prepare incremental on top of full
xtrabackup --prepare --apply-log-only --target-dir=/backups/full-20260412
xtrabackup --prepare --target-dir=/backups/full-20260412 \
--incremental-dir=/backups/incr-20260412
# Restore
systemctl stop mysqld
xtrabackup --copy-back --target-dir=/backups/full-20260412
chown -R mysql:mysql /var/lib/mysql
systemctl start mysqldLog binárioMySQL — Recuperação pontual
Os logs bináriosMySQL registram cada instrução de modificação de dados ou alteração de linha. Quando combinados com um backup completo ou físico, eles permitem a recuperação pontual a qualquer momento após a realização do backup.
# Enable binary logging in my.cnf
[mysqld]
server-id = 1
log-bin = /var/log/mysql/mysql-bin
binlog_format = ROW
binlog_expire_logs_seconds = 604800 # 7 days
sync_binlog = 1
gtid_mode = ON
enforce_gtid_consistency = ON
# Flush and archive binary logs
mysqladmin flush-logs
mysqlbinlog --read-from-remote-server --host=db-primary \
--raw --stop-never --result-file=/backup/binlogs/ \
mysql-bin.000042
# Point-in-time recovery: replay binlog up to specific timestamp
mysqlbinlog --stop-datetime="2026-04-12 14:30:00" \
/backup/binlogs/mysql-bin.000042 \
/backup/binlogs/mysql-bin.000043 | mysql -u rootMétodos de backupPostgreSQL
O ecossistema de backup doPostgreSQL é indiscutivelmente mais rico que o do MySQL, com ferramentas originais e um conjunto maduro de soluções comunitárias desenvolvidas especificamente para ambientes de produção em grande escala.
pg_dump e pg_dumpall — Backups lógicos
Assim como omysqldump, opg_dumpproduz backups lógicos. O formato personalizado (-Fc) é o padrão recomendado porque suporta restauração paralela, restauração seletiva de tabela e compactação integrada.
# Custom format with parallel dump (4 worker jobs)
pg_dump -Fc -j4 -f /backups/production-$(date +%Y%m%d).dump production_db
# Directory format for maximum parallelism on large databases
pg_dump -Fd -j8 -f /backups/production-$(date +%Y%m%d)/ production_db
# All databases including globals (roles, tablespaces)
pg_dumpall > /backups/pg-all-$(date +%Y%m%d).sql
# Parallel restore from custom format
pg_restore -j4 -d production_db_restored /backups/production-20260412.dump
# Selective restore: single table
pg_restore -j4 -d production_db -t orders /backups/production-20260412.dumppg_basebackup — Fundação de backup físico
pg_basebackupfaz uma cópia física de todo o diretório de dados PostgreSQL. É a base para recuperação autônoma e configuração de replicação de streaming. Combinado com o arquivamento WAL, permite a recuperação pontual.
# Physical backup with WAL files included
pg_basebackup -D /backups/base-$(date +%Y%m%d) \
-Ft -z -Xs -P -c fast \
-U replication_user -h db-primary
# Stream backup directly to a tar archive with checksums
pg_basebackup -D - -Ft -Xs -c fast \
-U replication_user -h db-primary | \
gzip > /backups/pg-base-$(date +%Y%m%d).tar.gzpgBackRest — Gerenciamento de backup de nível empresarial
pgBackRest é o padrão ouro para gerenciamento de backup PostgreSQL. Ele oferece suporte a backups completos, incrementais e diferenciais, backup e restauração paralelos, criptografia, destinos de vários repositórios (disco local, S3, GCS, Azure Blob) e arquivamento WAL automatizado — tudo por meio de uma configuração única e coesa.
# /etc/pgbackrest/pgbackrest.conf
[global]
repo1-type=s3
repo1-s3-bucket=pg-backups-production
repo1-s3-endpoint=s3.eu-west-1.amazonaws.com
repo1-s3-region=eu-west-1
repo1-s3-key=AKIAIOSFODNN7EXAMPLE
repo1-s3-key-secret=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
repo1-path=/pgbackrest
repo1-retention-full=4
repo1-retention-diff=14
repo1-cipher-type=aes-256-cbc
repo1-cipher-pass=a_very_secure_encryption_passphrase
# Second repository for geographic redundancy
repo2-type=azure
repo2-azure-container=pg-backups-dr
repo2-azure-account=prodbackupstorage
repo2-azure-key=base64encodedkeyhere
repo2-path=/pgbackrest
repo2-retention-full=2
process-max=4
compress-type=zst
compress-level=6
log-level-console=info
log-level-file=detail
start-fast=y
[production]
pg1-path=/var/lib/postgresql/16/main
pg1-port=5432
# PostgreSQL WAL archive configuration (postgresql.conf)
# archive_mode = on
# archive_command = 'pgbackrest --stanza=production archive-push %p'
# Create the stanza
pgbackrest --stanza=production stanza-create
# Verify the stanza configuration
pgbackrest --stanza=production check
# Full backup
pgbackrest --stanza=production --type=full backup
# Differential backup
pgbackrest --stanza=production --type=diff backup
# Incremental backup
pgbackrest --stanza=production --type=incr backup
# List backups
pgbackrest --stanza=production infoWAL-G — Arquivamento WAL leve para armazenamento em nuvem
WAL-G é uma alternativa mais simples ao pgBackRest que se concentra no streaming de segmentos WAL e backups básicos para armazenamento de objetos em nuvem. É popular em ambientes em contêineres onde você deseja um único binário com configuração mínima.
# Environment variables for WAL-G with S3
export WALG_S3_PREFIX=s3://pg-wal-archive/production
export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
export AWS_REGION=eu-west-1
export WALG_COMPRESSION_METHOD=lz4
export PGHOST=/var/run/postgresql
# Configure WAL archiving in postgresql.conf
# archive_mode = on
# archive_command = 'wal-g wal-push %p'
# restore_command = 'wal-g wal-fetch %f %p'
# Take a base backup
wal-g backup-push /var/lib/postgresql/16/main
# List backups
wal-g backup-list
# Restore from latest backup
wal-g backup-fetch /var/lib/postgresql/16/main LATEST
# Delete old backups (retain last 4)
wal-g delete retain FULL 4 --confirmBarman — Servidor de backup centralizado
OBarman (Backup and Recovery Manager) foi projetado para ambientes onde um servidor de backup dedicado gerencia backups para múltiplas instâncias PostgreSQL. Ele suporta protocolos de replicação rsync/SSH e streaming para transporte de backup.
# /etc/barman.d/production.conf
[production]
description = "Production PostgreSQL 16"
ssh_command = ssh postgres@db-primary
conninfo = host=db-primary user=barman dbname=postgres
streaming_conninfo = host=db-primary user=streaming_barman
backup_method = postgres
streaming_archiver = on
slot_name = barman
retention_policy = RECOVERY WINDOW OF 14 DAYS
# Create replication slot and start streaming
barman receive-wal --create-slot production
barman switch-wal --force --archive production
# Take a backup
barman backup production
# List backups
barman list-backup production
# Restore to point in time
barman recover --target-time "2026-04-12 14:30:00" \
production 20260412T120000 /var/lib/postgresql/16/mainRecuperação pontual (PITR)
A recuperação pontual doé o recurso mais importante em seu kit de ferramentas de backup. Ele permite restaurar um banco de dados para o estado exato em que se encontrava em qualquer momento específico — não apenas quando os backups foram feitos, mas a qualquer segundo entre os backups. Isso é fundamental para a recuperação de exclusão acidental de dados, bugs de aplicativos que corrompem dados e incidentes de segurança em que você precisa identificar o momento exato do comprometimento.
PITR para PostgreSQL
PostgreSQL PITR funciona restaurando um backup base e reproduzindo segmentos WAL até o carimbo de data/hora de destino. Com o pgBackRest, todo o processo é um único comando.
# Restore to specific point in time with pgBackRest
pgbackrest --stanza=production \
--type=time --target="2026-04-12 16:42:30" \
--target-action=promote \
restore
# Manual PITR using pg_basebackup + WAL archive
# 1. Stop PostgreSQL
systemctl stop postgresql
# 2. Clear the data directory and restore base backup
rm -rf /var/lib/postgresql/16/main/*
tar xzf /backups/pg-base-20260412.tar.gz -C /var/lib/postgresql/16/main/
# 3. Create recovery signal and configure restore
cat > /var/lib/postgresql/16/main/postgresql.auto.conf << 'CONF'
restore_command = 'cp /backup/wal_archive/%f %p'
recovery_target_time = '2026-04-12 16:42:30'
recovery_target_action = 'promote'
CONF
touch /var/lib/postgresql/16/main/recovery.signal
# 4. Start PostgreSQL — it will replay WAL and stop at target
systemctl start postgresqlPITR para MySQL
MySQL PITR combina uma restauração física XtraBackup com reprodução de log binário.
# 1. Restore the XtraBackup
systemctl stop mysqld
rm -rf /var/lib/mysql/*
xtrabackup --prepare --target-dir=/backups/full-20260412
xtrabackup --copy-back --target-dir=/backups/full-20260412
chown -R mysql:mysql /var/lib/mysql
systemctl start mysqld
# 2. Identify the binlog position from XtraBackup metadata
cat /backups/full-20260412/xtrabackup_binlog_info
# Output: mysql-bin.000042 154 3E11FA47-1111-1111-1111-AAAAAAAAAAAA:1-1234
# 3. Replay binlogs up to target time
mysqlbinlog --start-position=154 \
--stop-datetime="2026-04-12 16:42:30" \
/backup/binlogs/mysql-bin.000042 \
/backup/binlogs/mysql-bin.000043 | mysql -u rootArquitetura de backup multinuvemAs estratégias de backup de produção dodevem levar em conta a falha de qualquer provedor de nuvem ou região. Armazenar backups exclusivamente na mesma conta de nuvem e região que seu banco de dados de produção significa que um comprometimento de uma única conta, um problema de faturamento ou uma interrupção regional pode prejudicar seus dados e seus backups simultaneamente. Uma arquitetura robusta envia backups para pelo menos dois destinos de armazenamento independentes com replicação entre regiões.
Configuração de armazenamento em nuvemcom políticas de ciclo de vida
# AWS S3 lifecycle policy (Terraform)
resource "aws_s3_bucket_lifecycle_configuration" "backup_lifecycle" {
bucket = aws_s3_bucket.db_backups.id
rule {
id = "backup-tiering"
status = "Enabled"
transition {
days = 30
storage_class = "STANDARD_IA"
}
transition {
days = 90
storage_class = "GLACIER"
}
transition {
days = 365
storage_class = "DEEP_ARCHIVE"
}
expiration {
days = 2555 # 7 years for compliance
}
}
rule {
id = "wal-segments"
status = "Enabled"
filter {
prefix = "wal-archive/"
}
expiration {
days = 30
}
}
}
# Enable cross-region replication
resource "aws_s3_bucket_replication_configuration" "backup_replication" {
bucket = aws_s3_bucket.db_backups.id
role = aws_iam_role.replication.arn
rule {
id = "cross-region-dr"
status = "Enabled"
destination {
bucket = aws_s3_bucket.db_backups_dr.arn
storage_class = "STANDARD_IA"
encryption_configuration {
replica_kms_key_id = aws_kms_key.dr_key.arn
}
}
source_selection_criteria {
sse_kms_encrypted_objects {
status = "Enabled"
}
}
}
}# Azure Blob immutability policy (Azure CLI)
az storage container immutability-policy create \
--account-name prodbackupstorage \
--container-name pg-backups \
--period 365 \
--allow-protected-append-writes true
# GCS lifecycle with nearline transition
gsutil lifecycle set /dev/stdin gs://pg-backups-production << 'JSON'
{
"rule": [
{"action": {"type": "SetStorageClass", "storageClass": "NEARLINE"}, "condition": {"age": 30}},
{"action": {"type": "SetStorageClass", "storageClass": "COLDLINE"}, "condition": {"age": 90}},
{"action": {"type": "Delete"}, "condition": {"age": 2555}}
]
}
JSONCriptografia e segurança de backupOs backupssão um alvo de alto valor para invasores. Um backup não criptografado roubado fornece ao invasor uma cópia completa dos seus dados de produção sem a necessidade de violar os sistemas em execução. Cada backup, em trânsito e em repouso, deve ser criptografado.
Criptografia em trânsitosignifica usar TLS para todas as conexões entre o servidor de banco de dados e o destino de backup. Tanto o pgBackRest quanto o XtraBackup transmitem em canais criptografados quando configurados com TLS. Ao enviar para S3, Azure Blob ou GCS, os SDKs do cliente usam HTTPS por padrão.
Criptografia em repouso Opossui duas camadas. A criptografia do lado do servidor (SSE) criptografa os dados depois que eles chegam ao destino de armazenamento — S3 SSE-KMS, criptografia do serviço de armazenamento Azure ou criptografia padrão do GCS. A criptografia do lado do cliente criptografa os dados antes que eles saiam do servidor de banco de dados, garantindo que o provedor de nuvem nunca veja dados em texto simples. pgBackRest e WAL-G oferecem suporte nativo à criptografia do lado do cliente.
# pgBackRest client-side encryption config
repo1-cipher-type=aes-256-cbc
repo1-cipher-pass=long_random_passphrase_stored_in_vault
# WAL-G client-side encryption
export WALG_LIBSODIUM_KEY=$(cat /etc/wal-g/encryption.key)
# Or GPG-based
export WALG_GPG_KEY_ID=backup@example.com
# MySQL XtraBackup with encryption
xtrabackup --backup --encrypt=AES256 \
--encrypt-key-file=/etc/mysql/backup-encryption.key \
--target-dir=/backups/full-encrypted-$(date +%Y%m%d)Armazene chaves de criptografia separadamente dos próprios backups. Um gerenciador de segredos como HashiCorp Vault, AWS Secrets Manager ou Azure Key Vault é a abordagem recomendada. Se você armazenar a chave junto com o backup, um invasor que obtiver acesso ao seu armazenamento de backup terá tudo o que precisa.
Agendamento de backup automatizadoOs backups manuais donão são backups – são aspirações. Os agendamentos de backup devem ser automatizados, monitorados e alertar em caso de falha.
Cron do sistemapara Bare Metal e VMs
# /etc/cron.d/database-backups
# PostgreSQL — pgBackRest
# Full backup every Sunday at 01:00
0 1 * * 0 postgres pgbackrest --stanza=production --type=full backup 2>&1 | logger -t pgbackrest-full
# Differential backup every day at 01:00 (except Sunday)
0 1 * * 1-6 postgres pgbackrest --stanza=production --type=diff backup 2>&1 | logger -t pgbackrest-diff
# MySQL — XtraBackup
# Full backup every Sunday at 02:00
0 2 * * 0 root /usr/local/bin/mysql-backup.sh full 2>&1 | logger -t xtrabackup-full
# Incremental backup every day at 02:00 (except Sunday)
0 2 * * 1-6 root /usr/local/bin/mysql-backup.sh incremental 2>&1 | logger -t xtrabackup-incr
# Backup verification — restore test every Wednesday at 04:00
0 4 * * 3 root /usr/local/bin/verify-backup.sh 2>&1 | logger -t backup-verifyKubernetes CronJobs
Em ambientes Kubernetes, CronJobs substituem o cron do sistema. Eles oferecem lógica de repetição integrada, controle de simultaneidade e integração com o RBAC do cluster e gerenciamento de segredos.
apiVersion: batch/v1
kind: CronJob
metadata:
name: pg-backup-full
namespace: database
spec:
schedule: "0 1 * * 0" # Sunday 01:00 UTC
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 4
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 2
activeDeadlineSeconds: 7200 # 2 hour timeout
template:
spec:
serviceAccountName: db-backup
restartPolicy: OnFailure
containers:
- name: pgbackrest
image: pgbackrest/pgbackrest:2.50
command:
- /bin/bash
- -c
- |
pgbackrest --stanza=production --type=full backup
RESULT=$?
if [ $RESULT -eq 0 ]; then
curl -s -X POST "$SLACK_WEBHOOK" \
-d '{"text":"PostgreSQL full backup completed successfully"}'
else
curl -s -X POST "$SLACK_WEBHOOK" \
-d '{"text":"ALERT: PostgreSQL full backup FAILED"}'
fi
exit $RESULT
envFrom:
- secretRef:
name: pgbackrest-credentials
- secretRef:
name: slack-webhook
resources:
requests:
memory: 512Mi
cpu: 500m
limits:
memory: 2Gi
cpu: "2"
volumeMounts:
- name: pgbackrest-config
mountPath: /etc/pgbackrest
volumes:
- name: pgbackrest-config
configMap:
name: pgbackrest-conf
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: pg-backup-diff
namespace: database
spec:
schedule: "0 1 * * 1-6" # Mon-Sat 01:00 UTC
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 7
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 2
activeDeadlineSeconds: 3600
template:
spec:
serviceAccountName: db-backup
restartPolicy: OnFailure
containers:
- name: pgbackrest
image: pgbackrest/pgbackrest:2.50
command:
- /bin/bash
- -c
- |
pgbackrest --stanza=production --type=diff backup
envFrom:
- secretRef:
name: pgbackrest-credentials
resources:
requests:
memory: 256Mi
cpu: 250m
limits:
memory: 1Gi
cpu: "1"
volumeMounts:
- name: pgbackrest-config
mountPath: /etc/pgbackrest
volumes:
- name: pgbackrest-config
configMap:
name: pgbackrest-conf
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: mysql-backup-full
namespace: database
spec:
schedule: "0 2 * * 0"
concurrencyPolicy: Forbid
jobTemplate:
spec:
backoffLimit: 2
activeDeadlineSeconds: 7200
template:
spec:
serviceAccountName: db-backup
restartPolicy: OnFailure
containers:
- name: xtrabackup
image: percona/percona-xtrabackup:8.0
command:
- /bin/bash
- -c
- |
xtrabackup --backup --stream=xbstream --compress \
--user=$MYSQL_BACKUP_USER \
--password=$MYSQL_BACKUP_PASS \
--host=mysql-primary.database.svc | \
aws s3 cp - s3://$S3_BUCKET/mysql/full-$(date +%Y%m%d).xbstream
envFrom:
- secretRef:
name: mysql-backup-credentials
- secretRef:
name: aws-credentials
resources:
requests:
memory: 512Mi
cpu: 500m
limits:
memory: 2Gi
cpu: "2"Abordagens de backup nativoKubernetes
A execução de bancos de dados no Kubernetes introduz uma nova dimensão à estratégia de backup. Além dos backups de banco de dados em nível de aplicativo, você precisa considerar o backup em nível de cluster (etcd), instantâneos de volumes persistentes e backups gerenciados pelo operador.
Velero para backup em nível de cluster
Velero faz backup de recursos Kubernetes (implantações, serviços, mapas de configuração, segredos) e volumes persistentes. Ele não substitui backups de banco de dados em nível de aplicativo — ele captura um instantâneo consistente de falhas de PVs, que pode não ser transacionalmente consistente para bancos de dados. Use Velero para recuperação de cluster e ferramentas em nível de aplicativo (pgBackRest, XtraBackup) para recuperação de banco de dados.
# Install Velero with S3 backend
velero install \
--provider aws \
--bucket velero-backups \
--secret-file ./credentials-velero \
--backup-location-config region=eu-west-1 \
--snapshot-location-config region=eu-west-1 \
--use-volume-snapshots=true \
--plugins velero/velero-plugin-for-aws:v1.9.0
# Create a scheduled backup of the database namespace
velero schedule create db-namespace-backup \
--schedule="0 3 * * *" \
--include-namespaces database \
--ttl 720h \
--storage-location default \
--volume-snapshot-locations default
# On-demand backup before maintenance
velero backup create pre-maintenance-$(date +%Y%m%d) \
--include-namespaces database,monitoring \
--wait
# Restore a namespace from backup
velero restore create --from-backup pre-maintenance-20260412 \
--include-namespaces databaseInstantâneos do Longhornna k3s
As implantaçõesk3s geralmente usam Longhorn como provedor de armazenamento CSI. O Longhorn fornece snapshots em nível de volume e a capacidade de replicar snapshots para armazenamento de objetos compatível com S3. Para bancos de dados, combine snapshots do Longhorn com backups em nível de aplicativo para defesa profunda.
# Longhorn recurring snapshot job via CRD
apiVersion: longhorn.io/v1beta2
kind: RecurringJob
metadata:
name: pg-data-snapshot
namespace: longhorn-system
spec:
cron: "0 */4 * * *" # every 4 hours
task: snapshot
retain: 6
concurrency: 1
groups:
- pg-data
labels:
app: postgresql
---
# Longhorn recurring backup to S3
apiVersion: longhorn.io/v1beta2
kind: RecurringJob
metadata:
name: pg-data-s3-backup
namespace: longhorn-system
spec:
cron: "0 2 * * *" # daily at 02:00
task: backup
retain: 14
concurrency: 1
groups:
- pg-data
labels:
app: postgresql
---
# VolumeSnapshot using Longhorn CSI
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: pg-data-snap-$(date +%Y%m%d)
namespace: database
spec:
volumeSnapshotClassName: longhorn-snapshot-vsc
source:
persistentVolumeClaimName: pg-data-postgresql-0Backups gerenciados pelo operadorOperadores de banco de dadoscomo CloudNativePG (para PostgreSQL) e Percona Operator (para MySQL) integram o gerenciamento de backup diretamente no ciclo de vida do banco de dados. O operador lida com agendamento, arquivamento WAL e retenção automaticamente por meio de recursos personalizados.
# CloudNativePG cluster with integrated backup
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: production-pg
namespace: database
spec:
instances: 3
storage:
size: 100Gi
storageClass: longhorn
backup:
barmanObjectStore:
destinationPath: s3://pg-backups/cnpg/
endpointURL: https://s3.eu-west-1.amazonaws.com
s3Credentials:
accessKeyId:
name: aws-creds
key: ACCESS_KEY_ID
secretAccessKey:
name: aws-creds
key: SECRET_ACCESS_KEY
wal:
compression: gzip
maxParallel: 4
data:
compression: gzip
retentionPolicy: "30d"
---
apiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata:
name: production-pg-weekly
namespace: database
spec:
schedule: "0 1 * * 0"
backupOwnerReference: self
cluster:
name: production-pgEstratégias específicas do provedor de nuvemAWS - RDS e Aurora
OAWS RDS fornece snapshots diários automatizados com retenção configurável (até 35 dias) e backup contínuo por meio de arquivamento de log de transações para recuperação pontual. Aurora adiciona Backtrack, que pode retroceder o cluster para qualquer ponto dentro da janela de retrocesso sem exigir uma restauração – ele simplesmente reverte o estado interno do banco de dados.
# Enable automated backups with maximum retention (Terraform)
resource "aws_db_instance" "production" {
identifier = "production-pg"
engine = "postgres"
engine_version = "16.2"
instance_class = "db.r6g.xlarge"
allocated_storage = 500
backup_retention_period = 35 # maximum
backup_window = "03:00-04:00"
copy_tags_to_snapshot = true
deletion_protection = true
storage_encrypted = true
kms_key_id = aws_kms_key.rds.arn
# Enable PITR
enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]
}
# Cross-region automated backup replication
resource "aws_db_instance_automated_backups_replication" "dr" {
source_db_instance_arn = aws_db_instance.production.arn
kms_key_id = aws_kms_key.dr_rds.arn
retention_period = 14
}
# Aurora Backtrack (MySQL-compatible Aurora only)
resource "aws_rds_cluster" "aurora_production" {
cluster_identifier = "aurora-production"
engine = "aurora-mysql"
engine_version = "8.0.mysql_aurora.3.05.2"
backtrack_window = 86400 # 24 hours of backtrack
backup_retention_period = 35
preferred_backup_window = "03:00-04:00"
storage_encrypted = true
}
# Manual snapshot with cross-region copy
aws rds create-db-snapshot \
--db-instance-identifier production-pg \
--db-snapshot-identifier pre-migration-$(date +%Y%m%d)
aws rds copy-db-snapshot \
--source-db-snapshot-identifier arn:aws:rds:eu-west-1:123456789:snapshot:pre-migration-20260412 \
--target-db-snapshot-identifier pre-migration-20260412-dr \
--source-region eu-west-1 \
--region us-east-1 \
--kms-key-id arn:aws:kms:us-east-1:123456789:key/dr-key-idAzure — Servidor Flexível
O banco de dadosAzure para servidores flexíveis PostgreSQL e MySQL inclui backups automáticos com armazenamento localmente redundante ou com redundância geográfica. O backup com redundância geográfica permite a restauração entre regiões para recuperação de desastres.
# Azure Flexible Server with geo-redundant backup (Terraform)
resource "azurerm_postgresql_flexible_server" "production" {
name = "production-pg"
location = "westeurope"
resource_group_name = azurerm_resource_group.db.name
sku_name = "GP_Standard_D4s_v3"
version = "16"
storage_mb = 524288 # 512 GB
backup_retention_days = 35
geo_redundant_backup_enabled = true
authentication {
active_directory_auth_enabled = true
password_auth_enabled = false
}
}
# Azure Blob immutability for self-managed backups
resource "azurerm_storage_management_policy" "backup_lifecycle" {
storage_account_id = azurerm_storage_account.backups.id
rule {
name = "backup-tiering"
enabled = true
filters {
blob_types = ["blockBlob"]
prefix_match = ["pg-backups/"]
}
actions {
base_blob {
tier_to_cool_after_days_since_modification_greater_than = 30
tier_to_archive_after_days_since_modification_greater_than = 90
delete_after_days_since_modification_greater_than = 2555
}
}
}
}GCP— Nuvem SQL
OCloud SQL fornece backups automatizados e recuperação pontual prontos para uso. Para bancos de dados autogerenciados no GCE ou GKE, o GCS com classes de armazenamento nearline e coldline oferece armazenamento de backup econômico de longo prazo.
# Cloud SQL with automated backups and PITR (Terraform)
resource "google_sql_database_instance" "production" {
name = "production-pg"
database_version = "POSTGRES_16"
region = "europe-west1"
settings {
tier = "db-custom-4-16384"
backup_configuration {
enabled = true
start_time = "03:00"
point_in_time_recovery_enabled = true
transaction_log_retention_days = 7
backup_retention_settings {
retained_backups = 30
retention_unit = "COUNT"
}
}
ip_configuration {
ssl_mode = "ENCRYPTED_ONLY"
}
}
}
# Export to GCS for long-term retention
gcloud sql export sql production-pg \
gs://pg-backups-longterm/export-$(date +%Y%m%d).sql.gz \
--database=production_db \
--offloadVerificação de backup e teste de restauraçãoUm backup que nunca foi testado não é um backup – é uma esperança. Os testes de restauração automatizados devem ser executados regularmente, de preferência semanalmente, em um ambiente isolado. O teste deve validar não apenas que a restauração foi concluída sem erros, mas também que os dados restaurados são consistentes e que o aplicativo pode conectá-los e consultá-los.
#!/bin/bash
# verify-backup.sh — Automated backup verification script
set -euo pipefail
RESTORE_DIR="/tmp/backup-verify-$(date +%Y%m%d-%H%M%S)"
LOG_FILE="/var/log/backup-verify.log"
SLACK_WEBHOOK="${SLACK_WEBHOOK_URL}"
DB_TYPE="${1:-postgresql}" # postgresql or mysql
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"; }
alert() {
log "ALERT: $*"
curl -s -X POST "$SLACK_WEBHOOK" \
-H 'Content-Type: application/json' \
-d "{\"text\":\"BACKUP VERIFY FAILED: $*\"}"
}
cleanup() {
log "Cleaning up $RESTORE_DIR"
rm -rf "$RESTORE_DIR"
if [ "$DB_TYPE" = "postgresql" ]; then
pg_ctlcluster 16 verify stop 2>/dev/null || true
else
mysqladmin -S /tmp/mysql-verify.sock shutdown 2>/dev/null || true
fi
}
trap cleanup EXIT
mkdir -p "$RESTORE_DIR"
if [ "$DB_TYPE" = "postgresql" ]; then
log "Starting PostgreSQL backup verification"
# Restore latest pgBackRest backup to temporary directory
pgbackrest --stanza=production \
--pg1-path="$RESTORE_DIR/pgdata" \
--type=immediate \
--target-action=promote \
restore 2>&1 | tee -a "$LOG_FILE"
if [ ${PIPESTATUS[0]} -ne 0 ]; then
alert "pgBackRest restore failed"
exit 1
fi
# Start PostgreSQL on a different port
pg_ctlcluster 16 verify start -- \
-D "$RESTORE_DIR/pgdata" \
-o "-p 5433" \
-o "-c listen_addresses=127.0.0.1"
sleep 5
# Verify data integrity
TABLES=$(psql -p 5433 -d production_db -t -c \
"SELECT count(*) FROM information_schema.tables WHERE table_schema='public';")
log "Verified $TABLES tables exist"
ROW_CHECK=$(psql -p 5433 -d production_db -t -c \
"SELECT count(*) FROM orders WHERE created_at > now() - interval '7 days';")
log "Recent orders count: $ROW_CHECK"
if [ "$ROW_CHECK" -lt 1 ]; then
alert "PostgreSQL restore has no recent data — possible stale backup"
exit 1
fi
# Run pg_amcheck for corruption detection (PostgreSQL 14+)
pg_amcheck -p 5433 -d production_db --heapallindexed 2>&1 | tee -a "$LOG_FILE"
log "PostgreSQL backup verification PASSED"
else
log "Starting MySQL backup verification"
# Prepare and restore latest XtraBackup
LATEST_FULL=$(ls -td /backups/full-* | head -1)
cp -r "$LATEST_FULL" "$RESTORE_DIR/mysql-data"
xtrabackup --prepare --target-dir="$RESTORE_DIR/mysql-data"
# Start MySQL on a different socket and port
mysqld --datadir="$RESTORE_DIR/mysql-data" \
--socket=/tmp/mysql-verify.sock \
--port=3307 \
--skip-networking=0 \
--bind-address=127.0.0.1 &
sleep 10
# Verify data
TABLES=$(mysql -S /tmp/mysql-verify.sock -e \
"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='production_db';" -sN)
log "Verified $TABLES tables exist"
mysqlcheck -S /tmp/mysql-verify.sock --all-databases --check 2>&1 | tee -a "$LOG_FILE"
log "MySQL backup verification PASSED"
fi
curl -s -X POST "$SLACK_WEBHOOK" \
-H 'Content-Type: application/json' \
-d "{\"text\":\"Backup verification PASSED for $DB_TYPE at $(date)\"}"
log "Verification complete"Planejamento de recuperação de desastres: RTO e RPO
Cada estratégia de backup deve ser projetada em torno de duas métricas:Objetivo de tempo de recuperação (RTO)— quanto tempo você pode ficar inativo — eObjetivo de ponto de recuperação (RPO)— quantos dados você pode perder. Esses números orientam todas as decisões sobre frequência, método e arquitetura de backup.
| Cenário | RTO | RPO | Estratégia |
|---|---|---|---|
| Check-out de comércio eletrônico | < 5 min | 0 (perda zero de dados) | Replicação síncrona + arquivamento WAL contínuo + failover em espera ativa |
| SaaS aplicação | < 30 min | < 1 min | Replicação de streaming + arquivamento contínuo WAL-G + failover automatizado |
| Ferramentas internas | < 4 horas | < 1 hora | Diferencial diário + incremental horário + arquivamento WAL |
| Análise/armazenamento de dados | < 24 horas | < 24 horas | Backup completo diário para armazenamento em nuvem |
| Desenvolvimento/preparação | < 48 horas | < 1 semana | Backup completo semanal |
Para RPO zero, você precisa de replicação síncrona para pelo menos um modo de espera. Isto adiciona latência a cada transação de gravação, mas garante que nenhuma transação confirmada seja perdida. A maioria dos sistemas de produção aceita RPO (segundos de perda potencial) quase zero usando replicação de streaming assíncrona com arquivamento WAL/binlog contínuo, o que evita a penalidade de latência de gravação.
Modelo de runbook de recuperação de desastres# DR Runbook: Database Recovery
## Severity Levels
- P1: Complete data loss / corruption — all hands, CEO notified
- P2: Partial data loss / single region down — on-call team + escalation
- P3: Replica failure / backup failure — on-call investigation
## Recovery Procedures
### Scenario A: Primary DB failure, replicas healthy
1. Promote replica to primary (automated via Patroni / orchestrator)
2. Verify application connectivity
3. Re-establish replication from new primary
4. Investigate root cause
### Scenario B: Complete cluster failure, backups intact
1. Provision new database infrastructure
2. Restore latest full backup
3. Apply WAL/binlog to reach latest consistent point
4. Verify data integrity with checksums
5. Update DNS / connection strings
6. Resume application traffic
7. Re-establish backup schedule immediately
### Scenario C: Data corruption (bad migration / SQL injection)
1. Identify exact timestamp of corruption
2. Restore to point-in-time just before corruption
3. Export affected tables from restored copy
4. Merge clean data into production
5. OR: full PITR restore if corruption is widespread
## Contacts
- DBA on-call: [PagerDuty rotation]
- Infrastructure: [PagerDuty rotation]
- VP Engineering: [phone number]
## Validation Checklist
- [ ] Application health checks pass
- [ ] Row counts match expected ranges
- [ ] Recent transactions are present
- [ ] Replication re-established
- [ ] Backup schedule resumed
- [ ] Post-incident review scheduledMonitoramento e alerta de backupOs sistemas de backupfalham silenciosamente. O banco de dados continua em execução, o aplicativo continua atendendo ao tráfego e ninguém percebe que os backups pararam de funcionar há três semanas – até que precisem de um. O monitoramento proativo é essencial.
# Prometheus alerting rules for backup monitoring
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: backup-alerts
namespace: monitoring
spec:
groups:
- name: database-backups
rules:
- alert: BackupTooOld
expr: |
(time() - backup_last_successful_timestamp_seconds) > 90000
for: 10m
labels:
severity: critical
annotations:
summary: "Database backup is older than 25 hours"
description: "Last successful backup for {{ $labels.database }} was {{ $value | humanizeDuration }} ago"
- alert: BackupJobFailed
expr: |
kube_job_status_failed{job_name=~".*backup.*"} > 0
for: 5m
labels:
severity: critical
annotations:
summary: "Backup CronJob failed: {{ $labels.job_name }}"
- alert: WALArchivingLagging
expr: |
pg_stat_archiver_failed_count > 0
for: 5m
labels:
severity: warning
annotations:
summary: "PostgreSQL WAL archiving has failures"
- alert: BackupStorageQuotaNearing
expr: |
(backup_storage_used_bytes / backup_storage_quota_bytes) > 0.85
for: 30m
labels:
severity: warning
annotations:
summary: "Backup storage at {{ $value | humanizePercentage }} capacity"
- alert: BinlogSpaceCritical
expr: |
mysql_binlog_size_bytes > 53687091200
for: 10m
labels:
severity: warning
annotations:
summary: "MySQL binlog space exceeds 50GB — check archiving"# Custom Prometheus exporter for pgBackRest metrics
#!/usr/bin/env python3
"""pgBackRest Prometheus exporter — exposes backup age and size metrics."""
import json
import subprocess
import time
from prometheus_client import start_http_server, Gauge
BACKUP_AGE = Gauge('pgbackrest_last_backup_age_seconds', 'Seconds since last backup', ['stanza', 'type'])
BACKUP_SIZE = Gauge('pgbackrest_last_backup_size_bytes', 'Size of last backup', ['stanza', 'type'])
BACKUP_REPO_SIZE = Gauge('pgbackrest_repo_size_bytes', 'Total repository size', ['stanza'])
def collect():
result = subprocess.run(
['pgbackrest', '--output=json', 'info'],
capture_output=True, text=True
)
info = json.loads(result.stdout)
for stanza_info in info:
stanza = stanza_info['name']
for backup in stanza_info.get('backup', []):
backup_type = backup['type']
stop_time = backup['timestamp']['stop']
age = time.time() - stop_time
BACKUP_AGE.labels(stanza=stanza, type=backup_type).set(age)
size = backup['info']['size']
BACKUP_SIZE.labels(stanza=stanza, type=backup_type).set(size)
if __name__ == '__main__':
start_http_server(9854)
while True:
collect()
time.sleep(300)Erros comuns e antipadrões doDepois de gerenciar backups de bancos de dados em dezenas de ambientes de produção, esses são os erros que causam mais danos.
1. Backup no mesmo disco do banco de dados.Se o disco falhar, você perderá o banco de dados e o backup. Sempre grave backups em um destino de armazenamento separado – de preferência fora do host e fora da região.
2. Nunca testando restaurações.Um backup que não pode ser restaurado não é um backup. Agende testes de restauração automatizados semanalmente e peça aos engenheiros que realizem exercícios de restauração manuais trimestralmente.
3. Confiar exclusivamente na replicação como backup. A replicaçãonão é backup. UmDROP TABLEno primário é replicado instantaneamente para todas as réplicas. A replicação protege contra falhas de hardware e não contra erros lógicos.
4. Não monitorando tarefas de backup. As tarefas Cronfalham silenciosamente. Kubernetes CronJobs são suspensos. As credenciais S3 expiram. Cada tarefa de backup deve relatar sucesso ou falha a um sistema de monitoramento e alertar se o backup bem-sucedido mais recente for anterior ao seu RPO.
5. Armazenar chaves de criptografia juntamente com backups.Se um invasor obtiver acesso ao seu armazenamento de backup, ele também não deverá ter a chave de descriptografia. Armazene as chaves em um gerenciador de segredos dedicado.
6. Nenhuma política de retenção.Sem políticas de ciclo de vida, o armazenamento de backup cresce ilimitadamente. Defina janelas de retenção claras: 7 dias para segmentos WAL, 30 dias para backups diários, 12 meses para backups mensais e automatize sua exclusão.
7. Ignorando o impacto no desempenho do backup.A execução de um backup completo em um banco de dados primário durante horários de pico degrada o desempenho do aplicativo. Agende backups durante janelas de baixo tráfego ou faça backup a partir de uma réplica dedicada.
8. Usandomysqldumppara bancos de dados grandes sem--single-transaction.Sem esse sinalizador, omysqldumpbloqueia tabelas, bloqueando gravações durante o dump. Para bancos de dados grandes, isso pode significar minutos ou horas de inatividade.
9. Esquecendo de fazer backup da configuração do banco de dados.Restaurar dados é apenas metade da batalha. Se você perderpostgresql.conf,pg_hba.conf,my.cnf, configurações de replicação e concessões de usuário, não será possível trazer o banco de dados de volta a um estado utilizável. Inclua arquivos de configuração em seu processo de backup.
10. Não documentando o procedimento de restauração.Durante uma interrupção, a pessoa que restaura o banco de dados pode não ser a pessoa que configurou os backups. Um runbook escrito e testado é fundamental.
Otimização de custos dopara armazenamento de backup
Os custos de armazenamento de backup dopodem crescer rapidamente, especialmente com backups completos frequentes de grandes bancos de dados. Estas estratégias mantêm os custos sob controlo sem comprometer a recuperabilidade.
Use backups incrementais/diferenciais.Um backup completo semanal mais diferenciais diários usa uma fração do armazenamento em comparação com backups completos diários. A capacidade de restauração delta do pgBackRest significa que backups incrementais são restaurados quase tão rápido quanto backups completos.
Ativa a compactação.Algoritmos de compactação modernos como o zstd oferecem excelentes taxas de compactação (5:1 a 10:1 para dados típicos de banco de dados) com sobrecarga mínima do CPU. Tanto o pgBackRest quanto o XtraBackup suportam zstd nativamente.
Implementar camadas de armazenamento.Mova os backups para níveis de armazenamento cada vez mais baratos à medida que envelhecem. As políticas de ciclo de vida mostradas anteriormente (S3 Standard → IA → Glacier, GCS Standard → Nearline → Coldline) podem reduzir os custos de armazenamento de longo prazo em 70–90%.
Desduplicar sempre que possível.pgBackRest usa desduplicação em nível de bloco em seu repositório, armazenando apenas blocos alterados em backups. Isso reduz drasticamente o armazenamento de bancos de dados onde a maioria dos dados é estática.
Janelas de retenção do tamanho certo.Muitas equipes optam por manter backups para sempre por precaução. Analise seus padrões reais de recuperação e requisitos de conformidade e defina a retenção correspondente. Um requisito de retenção de 7 anos pode usar armazenamento de arquivo profundo por menos de US$ 1/TB/mês na maioria dos provedores de nuvem.
# Cost comparison: Full vs Incremental backup storage
# Assuming 500 GB database, 5% daily change rate, 30-day retention
# Strategy A: Daily full backups
# 500 GB × 30 days = 15,000 GB = 15 TB
# S3 Standard: 15 TB × $0.023/GB = $345/month
# Strategy B: Weekly full + daily incremental
# 4 full × 500 GB = 2,000 GB
# 26 incremental × 25 GB = 650 GB
# Total: 2,650 GB ≈ 2.6 TB
# S3 Standard: 2.6 TB × $0.023/GB = $59.80/month
# Strategy C: Strategy B + lifecycle tiering
# Current week: 525 GB Standard = $12.08
# Weeks 2-4: 2,125 GB Standard-IA = $26.56
# Total: $38.64/month
# Savings: Strategy C is 89% cheaper than Strategy AJuntando tudo: uma arquitetura de backup completa
Esta é a arquitetura de backup recomendada para um ambiente de produção executando MySQL e PostgreSQL, implantado em Kubernetes com recuperação de desastres em várias nuvens.
Para PostgreSQL:
- pgBackRest como ferramenta de backup primária com dois repositórios (S3 + Azure Blob)
- Arquivamento WAL contínuo em ambos os repositórios
- Backup completo semanal + diferencial diário (via K8s CronJob)
- Capturas instantâneas de volume do Longhorn a cada 4 horas para reversão rápida
- Verificação de restauração automatizada toda quarta-feira Monitoramento
- Prometheus com alertas sobre idade de backup, falha e armazenamento
Para MySQL:
- Percona XtraBackup para backups físicos transmitidos para S3
- Envio contínuo de log binário para armazenamento separado
- Completo semanal + incremental diário (via K8s CronJob)
- Diário
mysqldumpde esquemas críticos para backups lógicos portáteis Instantâneos de volume do Longhorn - a cada 4 horas
- Verificação de restauração automatizada toda quinta-feira
Para o cluster Kubernetes:
- Velero backup diário de namespaces de banco de dados com instantâneos PV Instantâneos do
- RKE2/k3s etcd a cada 6 horas para armazenamento fora do cluster Repositório
- GitOps como fonte da verdade para todos os manifestos
Para recuperação de desastres:
- Replicação entre regiões
- S3 para região DR
- Azure GRS para cópias de backup com redundância geográfica Drill mensal de DR
- : reconstrução completa do cluster a partir de backups na região de DR
- Runbook documentado com árvore de decisão para cada cenário de falha
são a última linha de defesa entre o seu negócio e a perda catastrófica de dados. As estratégias descritas neste artigo — backups físicos e lógicos, arquivamento WAL contínuo e binlog, armazenamento em várias nuvens com políticas de ciclo de vida, criptografia em todas as camadas, agendamento automatizado via Kubernetes CronJobs e verificação sistemática de restauração — representam o estado da arte atual para ambientes de produção MySQL e PostgreSQL.
A conclusão mais importante é esta:uma estratégia de backup é tão boa quanto seu último teste de restauração bem-sucedido. Cada ferramenta, script e padrão de arquitetura neste artigo existe para servir a um propósito: garantir que, quando o pior acontecer, você possa recuperar seus dados, cumprir seus compromissos de RTO e RPO e manter seu negócio funcionando. Construa seu sistema de backup, automatize-o, monitore-o, teste-o e teste-o novamente. Seu eu futuro agradecerá.