Alta disponibilidade
Replicação do MySQL atrasada: onde o lag realmente está
Seconds_Behind_Source engana com frequência. Como medir o atraso real da réplica MySQL e identificar se o gargalo é rede, aplicação ou transação.
Não confie no Seconds_Behind_Source para dizer se a réplica está em dia. Ele mede uma coisa específica — a diferença entre o relógio da réplica e o timestamp do evento que a thread de aplicação está processando neste instante — e essa não é a mesma pergunta que "quanto tempo atrás essa réplica reflete o primário". Em vários cenários comuns ele mostra zero enquanto a réplica está minutos atrás.
Este texto usa a terminologia do MySQL 8.0.22 em diante (SHOW REPLICA STATUS, Seconds_Behind_Source). Em versões anteriores, os comandos e campos equivalentes usam a nomenclatura antiga.
Por que o número mais usado engana
A replicação tem duas etapas independentes. A thread de I/O conecta no primário e copia eventos do binary log para o relay log local. A thread de aplicação (ou várias, quando há paralelismo) lê o relay log e executa.
Seconds_Behind_Source só observa a segunda etapa. Isso produz três situações onde ele mente:
- A thread de I/O parou ou está lenta. A réplica aplica rapidamente o pouco que tem no relay log e reporta zero, enquanto o backlog real está acumulando no primário.
- A réplica ficou ociosa. Sem eventos novos para aplicar, o valor vai a zero mesmo que a réplica tenha acabado de sair de um atraso grande.
- Há descompasso de relógio entre os servidores. O cálculo usa timestamps de máquinas diferentes.
O primeiro passo, então, é olhar as duas etapas separadamente.
SHOW REPLICA STATUS\G
Os campos que importam nessa saída: Replica_IO_Running e Replica_SQL_Running (ambos precisam estar Yes), Last_IO_Error e Last_SQL_Error, e a distância entre Read_Source_Log_Pos e Exec_Source_Log_Pos — que mostra quanto já chegou mas ainda não foi aplicado.
A medida honesta está no performance_schema
O MySQL registra, para cada transação, o momento em que ela foi commitada na origem e o momento em que a réplica terminou de aplicá-la. A diferença entre esses dois timestamps é o atraso real, ponta a ponta:
SELECT worker_id, APPLYING_TRANSACTION, APPLYING_TRANSACTION_ORIGINAL_COMMIT_TIMESTAMP AS commit_na_origem, APPLYING_TRANSACTION_START_APPLY_TIMESTAMP AS inicio_aplicacao, TIMESTAMPDIFF( SECOND, APPLYING_TRANSACTION_ORIGINAL_COMMIT_TIMESTAMP, NOW() ) AS atraso_sFROM performance_schema.replication_applier_status_by_worker;
replication_connection_status traz o lado da coleta, com os timestamps da última transação recebida — útil para separar "não chegou" de "chegou e não foi aplicado". Com as duas visões, o diagnóstico deixa de ser adivinhação: ou o gargalo está no transporte, ou está na aplicação.
Quando o gargalo é a aplicação
O padrão mais comum: o primário recebe escrita de muitas conexões em paralelo, e a réplica tenta reproduzir isso com poucas threads. A capacidade de escrita da réplica é estruturalmente menor que a do primário, e o atraso cresce nos horários de pico.
O paralelismo na réplica é controlado por replica_parallel_workers, e a forma como o MySQL decide o que pode rodar em paralelo depende de binlog_transaction_dependency_tracking no primário. Com o modo baseado em writeset, o primário anota quais transações tocaram linhas distintas, permitindo que a réplica as aplique simultaneamente com segurança. Sem isso, o paralelismo fica limitado ao que couber na mesma janela de commit.
Três outros fatores pesam nessa etapa:
Transação enorme. Um UPDATE que altera milhões de linhas viaja como um bloco e é aplicado por um único worker. Durante todo esse tempo, o atraso cresce e nenhum ajuste de paralelismo ajuda. Dividir a operação em lotes menores no lado da aplicação é o que resolve.
Tabela sem chave primária. Com replicação baseada em linha, a réplica precisa localizar cada linha afetada. Sem chave primária ou índice único, essa busca vira varredura completa por linha alterada — um UPDATE barato no primário pode custar horas na réplica. Vale auditar:
SELECT t.table_schema, t.table_nameFROM information_schema.tables AS tLEFT JOIN information_schema.table_constraints AS c ON c.table_schema = t.table_schema AND c.table_name = t.table_name AND c.constraint_type = 'PRIMARY KEY'WHERE t.table_schema NOT IN ('mysql','sys','information_schema','performance_schema') AND t.table_type = 'BASE TABLE' AND c.constraint_name IS NULL;
Durabilidade e I/O. Réplica com disco mais lento que o primário, ou com configuração de flush por transação em hardware que não a comporta, acumula atraso mesmo sem carga anormal.
O que o atraso significa para o negócio
Aqui a discussão sai da técnica. Se a réplica atende leitura da aplicação, o atraso é inconsistência visível: um pedido gravado e não encontrado na consulta seguinte. Se a réplica é o plano de continuidade, o atraso é perda de dados em caso de failover — o mesmo conceito de RPO tratado em seu backup restaura dentro do prazo do negócio?, aplicado à replicação em vez do backup.
Uma réplica com dez minutos de atraso não é uma réplica com um alerta amarelo. É uma réplica que, se assumir agora, perde dez minutos de transações.
Próximo passo
Réplica atrasada costuma ser tratada como métrica de infraestrutura, quando na prática define o pior caso de perda de dados do ambiente.
A H1 Data faz um diagnóstico gratuito do ambiente MySQL com leitura do atraso real de replicação, dos padrões de transação que o produzem e da configuração de durabilidade, com o que priorizar primeiro.