Replicação no MariaDB: como os dados são sincronizados entre servidores
Replicação é o processo de manter cópias sincronizadas de um banco de dados em mais de um servidor. Toda alteração feita no servidor de origem é propagada automaticamente para uma ou mais réplicas, que passam a refletir o mesmo estado dos dados.
No MariaDB, esse mecanismo é construído em cima de duas peças: o binary log (binlog), que registra tudo o que muda na origem, e o relay log, que recebe e reaplica essas mudanças na réplica. Este artigo explica como cada uma funciona, e quais configurações determinam se essa replicação é realmente confiável ou apenas aparenta ser.
Por que replicar um banco de dados
Antes do mecanismo, vale o motivo. As razões mais comuns para colocar réplicas em produção são:
- Alta disponibilidade: se o banco principal cair, existe uma cópia pronta para assumir.
- Distribuição de leitura: relatórios, dashboards e consultas pesadas rodam em uma réplica, sem competir por recursos com o tráfego de escrita.
- Backup sem impacto: um dump ou snapshot tirado da réplica não trava o banco de produção.
- Migração e upgrade com downtime mínimo: a nova versão sobe como réplica, é validada em paralelo, e só depois assume o tráfego.
- Arquiteturas Master-Master: múltiplos nós aceitando escrita ao mesmo tempo, cada um replicando para o outro, comum em sistemas que precisam de failover rápido sem reconfiguração manual de aplicação.
Cada um desses cenários muda decisões de configuração, principalmente as relacionadas à durabilidade dos dados, que veremos mais adiante.
O mecanismo central: o binary log (binlog)
O binlog é a peça fundamental de toda replicação no MariaDB. É um arquivo binário, sequencial, onde o servidor grava todo evento que modifica dados (inserts, updates, deletes, DDLs).
Ele não foi criado originalmente para replicação. A função original é point-in-time recovery, restaurar o banco para um instante exato do passado, mas é esse mesmo mecanismo que a replicação reaproveita.
Formatos de binlog
| Formato | O que grava | Característica |
|---|---|---|
STATEMENT |
O comando SQL executado | Mais compacto, mas pode divergir com funções não-determinísticas (NOW(), RAND(), UUID()) |
ROW |
O dado antes/depois de cada linha alterada | Maior, porém determinístico: garante que a réplica fique bit a bit igual à origem |
MIXED |
O servidor escolhe automaticamente entre os dois | Tenta equilibrar tamanho e segurança |
Em topologias Master-Master, o formato ROW é mais indicado: qualquer não-determinismo no STATEMENT seria amplificado pelos dois lados escrevendo ao mesmo tempo.
Anatomia de um evento no binlog
Cada evento carrega, entre outras coisas:
- O timestamp da execução
- O server_id de origem (quem gerou o evento)
- O tipo de evento (insert, update, delete, query, etc.)
- Os dados da linha (em
ROW) ou o texto do comando (emSTATEMENT)
O server_id é o detalhe que sustenta boa parte da lógica de replicação bidirecional, como vamos ver mais adiante.
Verificando o binlog
-- Está ativo?
SHOW VARIABLES LIKE 'log_bin%';
-- Posição atual de escrita
SHOW MASTER STATUS;
-- Todos os arquivos de binlog existentes
SHOW BINARY LOGS;
O SHOW MASTER STATUS retorna algo como File: db-bin.000007 | Position: 439689796. É exatamente essa dupla, arquivo + posição, que qualquer réplica usa para saber até onde já processou.
Relay log: como a réplica consome o binlog
Do lado da réplica, dois processos trabalham em conjunto:
- I/O Thread: conecta no servidor de origem, “puxa” os eventos do binlog remoto e grava localmente em um arquivo chamado relay log. É essencialmente uma cópia temporária do binlog de origem.
- SQL Thread (ou threads paralelas, dependendo da configuração): lê o relay log e reaplica cada evento no próprio banco, como se fossem comandos locais.
Esse desacoplamento entre “receber” e “aplicar” é o que permite que a réplica continue baixando eventos mesmo que a aplicação local esteja momentaneamente ocupada. É também de onde vem o campo Seconds_Behind_Master, que compara o horário do evento em execução com o relógio da réplica.
O que observar no status de uma réplica
SHOW REPLICA STATUS\G -- ou SHOW SLAVE STATUS\G
Alguns campos que merecem atenção:
Read_Master_Log_Pos(o que já foi baixado) vsExec_Master_Log_Pos(o que já foi aplicado): se estiverem muito distantes, há fila acumulada no relay log.Parallel_Mode: optimistic: as transações são aplicadas em paralelo assumindo que não vão conflitar entre si, o que melhora bastante o throughput em cargas com transações independentes. Detectado um conflito, a segunda transação sofre rollback e é reexecutada depois que a primeira termina. É o modo padrão desde a versão 10.5.1. Vale checar junto oslave_parallel_threads: em0, nenhuma thread de trabalho é criada e a aplicação segue serial dentro do SQL Thread, mesmo com o status exibindooptimistic.Seconds_Behind_Master: o indicador mais direto de saúde da replicação.
GTID x file+position: por que isso importa
O MariaDB tem sua própria implementação de GTID (Global Transaction ID), com sintaxe específica da engine.
| File + Position | GTID | |
|---|---|---|
| Identificação de um evento | Nome do arquivo + offset em bytes | ID único global, independente do arquivo |
| Troca de master (failover) | Manual: é preciso descobrir a posição exata onde a nova réplica deve começar | O servidor calcula sozinho a partir de onde continuar |
Ambientes mais antigos, ou que nunca precisaram automatizar failover, costumam continuar em file+position simplesmente porque funciona e ninguém migrou. Já GTID é a escolha natural para quem quer failover automatizado.
Topologias de replicação
Assim como em clusters, existem diferentes formas de organizar quem escreve e quem lê.
Master-Slave (Líder-Seguidor)
Escritas concentradas no master; réplicas atendem leituras. É a topologia mais simples e mais comum.
Master-Master (Bidirecional)
Escrita em qualquer nó, replicação nos dois sentidos. Vantagem: failover instantâneo, sem promoção manual de réplica. Risco: o dado que sai de A e chega em B não pode voltar para A, senão circularia para sempre. O MariaDB corta esse ciclo sozinho, e o que muda entre as duas configurações possíveis é o que cada nó grava no próprio binlog:
- Abordagem clássica: manter
log_slave_updates = ON, com a réplica regravando no próprio binlog o que recebeu. O loop não se forma porquereplicate_same_server_idvaleOFFpor padrão, e com isso todo servidor descarta os eventos que chegam carregando o próprioserver_id. Em anéis de três nós ou mais, é possível filtrar outros servidores explicitamente comCHANGE MASTER TO IGNORE_SERVER_IDS = (3,5), que barra os eventos antes mesmo do relay log. - Abordagem mais simples: manter
log_slave_updates = OFFnos dois nós. Assim, o que é recebido via replicação nunca é regravado no binlog local: só entra ali o que se originou localmente. O dado dá exatamente uma volta e para, sem precisar de filtro deserver_id. A limitação: não escala bem além de 2 nós em uma topologia em cadeia.
O detalhe que evita colisão de IDs: auto_increment
Em qualquer topologia com mais de um ponto de escrita ativo, existe um risco concreto: dois servidores gerando o mesmo valor de chave primária autoincremento ao mesmo tempo, causando erro de duplicidade e travando a replicação.
A solução padrão é fazer cada nó pular de N em N (onde N é o número de nós que escrevem), começando de um offset diferente:
# Nó 1
auto_increment_increment = 2
auto_increment_offset = 1
# Nó 2
auto_increment_increment = 2
auto_increment_offset = 2
Com isso, o Nó 1 gera sempre IDs ímpares (1, 3, 5, 7…) e o Nó 2 sempre pares (2, 4, 6, 8…). Nenhum dos dois vai gerar um valor que o outro também possa gerar, o que torna a colisão impossível mesmo com os dois aceitando escrita simultânea, sem nenhuma coordenação no momento do insert.
É uma configuração simples, mas essencial em qualquer topologia Master-Master. Sem ela, o primeiro pico de escrita simultânea nos dois nós vira erro de duplicate key e a replicação para.
Como o InnoDB escreve em disco
Até aqui, tratamos de o que é replicado. Agora vamos ver como esse dado chega ao disco.
Redo log
O InnoDB não escreve direto nos arquivos de dados (.ibd) a cada transação, porque isso seria lento demais. Em vez disso, ele grava primeiro em um redo log: um arquivo circular, de tamanho fixo, que registra as mudanças físicas nas páginas de dados. Esse log garante que, mesmo se o servidor cair antes de a página modificada ser persistida no arquivo definitivo, o InnoDB consiga refazer a mudança na inicialização seguinte.
No MariaDB, o tamanho do redo log é definido pela variável innodb_log_file_size. Desde a versão 10.5 existe apenas um arquivo de redo log (ib_logfile0, não mais o par antigo ib_logfile0/ib_logfile1), e desde a 10.9 essa variável é dinâmica: dá para alterar via SET GLOBAL sem reiniciar o servidor.
Doublewrite buffer
Uma página do InnoDB tem 16KB, mas o filesystem/disco pode gravar em blocos menores. Se o servidor cair no meio da escrita de uma página, ela pode ficar parcialmente gravada. É o problema conhecido como torn page, que o redo log sozinho não resolve, porque ele registra a mudança lógica, não a página inteira.
O doublewrite buffer (innodb_doublewrite = ON) resolve isso escrevendo cada página duas vezes: primeiro em um buffer sequencial, depois no local definitivo. Se o servidor cair no meio do processo, o InnoDB detecta a página corrompida e a restaura a partir da cópia no buffer.
Flush method
Com innodb_flush_method = O_DIRECT, o InnoDB escreve direto no disco, contornando o page cache do sistema operacional. Isso evita que o mesmo dado fique em memória duas vezes, uma no cache do SO e outra no buffer pool do InnoDB, o que importa especialmente em servidores com RAM limitada.
innodb_flush_log_at_trx_commit
Define quando o redo log vai para o disco. O padrão do MariaDB é 1: cada commit é gravado e sincronizado no disco. É o único valor totalmente ACID, e o único em que nada já confirmado se perde numa queda de energia. Com 2, a gravação vai para o cache do sistema operacional a cada commit e o flush real acontece uma vez por segundo, trocando até um segundo de transações por menos I/O. Na origem de uma replicação, mantenha 1.
sync_binlog
Define quando o binlog vai para o disco. O padrão do MariaDB é 0, ou seja, quem decide o momento do flush é o sistema operacional. Com 1, o binlog é sincronizado a cada escrita.
O risco é específico de replicação. Se a origem perde energia com sync_binlog = 0, a cauda do binlog que estava apenas em cache do SO desaparece, mas aquelas transações já foram enviadas e aplicadas na réplica. A origem volta com menos dados do que a réplica e passa a escrever em posições de binlog que a réplica já consumiu. O resultado é erro de duplicate key, no melhor caso, ou divergência silenciosa entre os dois bancos, no pior.
Revisando os conceitos
Binlog e relay log formam o mecanismo de transporte: o binlog registra tudo o que muda na origem, o relay log recebe essa informação na réplica antes de aplicá-la. É um modelo de “receber primeiro, aplicar depois” que existe justamente para desacoplar a velocidade da rede da velocidade de processamento local.
A topologia escolhida (Master-Slave ou Master-Master) define quem pode escrever e como o sistema se comporta em caso de falha. Master-Master ganha em failover instantâneo, mas exige cuidado extra com loops de replicação e colisão de IDs autoincremento.
Conclusão
Configurar uma replicação MariaDB básica é fácil: apontar uma réplica para o binlog de uma origem já é suficiente para ver dados fluindo. A parte difícil está nos detalhes cobertos neste artigo: o formato do binlog, a prevenção de loops em topologias bidirecionais, e as configurações de durabilidade que definem o que acontece no pior cenário possível.