Dev & EngARTIGO

Como uma cláusula WHERE seletiva pode travar um cluster Galera inteiro

Um incidente real mostra como o ajuste automático de chunks do pt-online-schema-change, pensado para acelerar migrações, pode sobrecarregar a replicação síncrona do Galera e travar o cluster inteiro.

Como uma cláusula WHERE seletiva pode travar um cluster Galera inteiro
Imagem gerada por IA

Um comando de manutenção que expôs um corner case

Em 28 de setembro de 2026, o Percona Database Blog publicou um relato de Corrado Pandiani sobre um incidente em produção envolvendo pt-online-schema-change, a ferramenta do Percona Toolkit usada para alterar tabelas grandes sem bloquear escritas. O caso aconteceu durante um atendimento a cliente rodando Percona XtraDB Cluster (PXC), a distribuição de MySQL↳MySQL8 conteúdosMySQL + Adminer + Docker Compose: montando rapidamente um ambiente para usoData · abr 2019Banco de dados MYSQL no PHPStormDev (Back & Front) · jul 2019Criando um ambiente de desenvolvimento PHP mínimo com Docker – Parte 3Dev (Back & Front) · out 2019Ver tudo em Data → com replicação síncrona via Galera.

O comando que disparou o problema parecia inofensivo: adicionar um índice a uma tabela de centenas de milhões de linhas, restringindo a cópia apenas às linhas recentes com uma cláusula --where.

pt-online-schema-change \
--alter "ADD INDEX idx_status(status)" \
--where "created_at >= NOW() - INTERVAL 30 DAY" \
D=test,t=events \
--execute --force

A lógica é comum: ignorar dados históricos, migrar só os registros ativos, reduzir o tempo total da operação. Funciona bem na maioria dos casos. Neste, quase derrubou o cluster.

Como o auto-resize de chunks funciona por baixo

pt-online-schema-change copia a tabela em pedaços (chunks) delimitados por faixas da chave primária, aplicando cada chunk como uma transação separada. Por padrão, a ferramenta ajusta o tamanho de cada chunk para manter o tempo de execução próximo do valor definido em --chunk-time (0.5 segundo por padrão): se um chunk roda rápido demais, o próximo fica maior; se fica lento, o próximo encolhe.

Esse comportamento adaptativo existe para equilibrar throughput e carga automaticamente, sem que o operador precise calibrar manualmente o tamanho ideal de chunk para cada tabela. Para uma varredura comum, com densidade de linhas uniforme, o algoritmo converge rápido para um valor estável.

O efeito oculto de um WHERE seletivo

O problema aparece quando a maior parte das linhas varridas é descartada pelo --where. No caso relatado, a tabela tinha da ordem de 1 bilhão de linhas, das quais apenas uma fração pequena batia com a condição created_at >= NOW() - INTERVAL 30 DAY:

LinhasBate com o WHERE
~990 milhõesNão
~10 milhõesSim

O detalhe crítico: o chunking do pt-osc sempre usa a chave primária como limite, e como created_at crescia junto com essa chave, todas as linhas que interessavam ficavam concentradas no fim do índice. Durante um longo trecho da varredura, quase toda linha lida era rejeitada, e a cópia avançava muito rápido.

O algoritmo adaptativo interpreta essa velocidade como sinal de que os chunks estão pequenos demais, e continua aumentando o próximo chunk. Quando a varredura finalmente alcança as linhas que batem com o --where, o tamanho de chunk já cresceu de forma desproporcional. Em vez de copiar alguns milhares de linhas por transação, o pt-osc passa a copiar centenas de milhares (ou mais) de uma vez só.

Por que isso quebra o Galera

Cada chunk copiado vira um write set replicado de forma síncrona para todos os nós do cluster. Write sets muito grandes produzem fases de certificação mais longas, filas de aplicação maiores e latência de replicação crescente. Quando essa latência ultrapassa o limite tolerado, o Galera ativa o Flow Control: o mecanismo que pausa commits em toda a aplicação para dar tempo ao nó mais lento de aplicar o que já recebeu.

Com Flow Control ativo, o throughput do cluster despenca. No relato da Percona, em situações extremas o cluster chegou a ficar praticamente congelado até a transação superdimensionada terminar de ser aplicada em todos os nós, exigindo reinício depois de dezenas de minutos travado.

O irônico é que o próprio mecanismo pensado para otimizar throughput, o auto-resize, acaba criando exatamente o padrão de carga que o Galera lida pior: transações grandes, síncronas e concentradas.

O InnoDB também sofre com transações grandes

O artigo da Percona mostra que, no nó que processava a escrita, o log de erros começou a registrar avisos como este assim que os primeiros chunks grandes entraram:

[Warning] [MY-014084] [InnoDB] Threads are unable to reserve space in redo log which can't be reclaimed
due to the 'log_checkpointer' consumer still lagging behind at LSN = 852886039088.
Consider increasing innodb_redo_log_capacity.

O aviso indica que o InnoDB recebeu uma transação grande demais para o espaço livre nos redo logs, forçando checkpoints mais frequentes e mais custosos. O mesmo tipo de mensagem apareceu depois nos outros nós, durante a fase de aplicação do write set replicado, o que ajuda a explicar por que o impacto não ficou restrito a um único servidor.

Os sintomas em produção

A lista de sintomas observados no incidente real dá uma ideia do quanto o efeito em cascata se espalha pelo cluster:

  • percentual de Flow Control subindo rapidamente;
  • crescimento de wsrep_local_recv_queue;
  • lag de replicação alto entre nós;
  • commits da aplicação parados;
  • uso de CPU em alta;
  • picos grandes no tempo de aplicação de transações;
  • checkpoints do InnoDB mais frequentes em todos os nós.

A migração de esquema em si costuma terminar com sucesso. O problema é o efeito colateral sobre o tráfego de produção enquanto ela roda.

As correções propostas

Em resumo: a solução mais simples e eficaz é desligar o auto-resize sempre que houver um --where seletivo. Basta fixar o tamanho do chunk em vez de deixá-lo variar:

--chunk-time=0
--max-flow-ctl=0

Com --chunk-time=0, o tamanho do chunk deixa de se ajustar automaticamente: o tempo de cada chunk varia, mas o número de linhas por chunk permanece constante. O mesmo efeito pode ser obtido definindo --chunk-size explicitamente e omitindo --chunk-time. O ganho é previsibilidade: a migração pode demorar um pouco mais, mas o Galera nunca recebe um write set fora do esperado.

O parâmetro --max-flow-ctl funciona como um segundo cinto de segurança. Com ele, o pt-osc monitora wsrep_flow_control_paused (a fração de tempo que o nó passou pausado por Flow Control) e suspende a cópia de linhas sempre que esse valor ultrapassa o limite definido, retomando quando o cluster volta a operar normalmente. Definir o valor próximo de zero faz a ferramenta parar de copiar linhas ao primeiro sinal de Flow Control em qualquer nó, em vez de tolerar alguma pausa antes de recuar, o que é a postura mais segura para um cluster de produção já mostrando sinais de estresse, como os avisos de atraso no checkpointer descritos acima.

Uma terceira alavanca é --chunk-index. Por padrão, o pt-osc faz o chunking pela chave primária (ou pelo índice único mais adequado que encontrar), o que no caso relatado significava seguir a mesma coluna monotonicamente crescente que o created_at, produzindo o longo trecho de chunks quase vazios. Forçar o chunking por um índice secundário sem correlação com a coluna do --where pode distribuir linhas relevantes e irrelevantes de forma mais uniforme entre os chunks.

A própria documentação do Percona Toolkit alerta, porém, que um índice de chunking mal escolhido prejudica o plano de execução das queries, e poucas tabelas têm um índice conveniente que seja seletivo o bastante e, ao mesmo tempo, descorrelacionado do --where. Por isso, o artigo recomenda tratar --chunk-index como otimização secundária, específica de cada tabela, mantendo --chunk-time=0 e --max-flow-ctl como proteção principal.

A quarta opção evita o --where por completo: dividir a operação em duas etapas. Primeiro, rodar pt-archiver só para apagar as linhas que não interessam mais; depois, rodar o pt-online-schema-change sobre a tabela já enxuta. Esse caminho em duas fases elimina de vez o risco de distribuição não uniforme das linhas que batem com a condição.

O que muda para quem opera Galera em produção

O caso reforça um princípio conhecido por quem trabalha com replicação síncrona: mecanismos adaptativos otimizados para um cenário (varredura uniforme) podem se tornar hostis em outro (varredura seletiva). Como coloca Pandiani no fechamento do artigo:

Às vezes, previsibilidade é uma otimização melhor do que agressividade.

Sometimes, predictability is a better optimization than aggressiveness.Corrado Pandiani, Percona

Antes de rodar pt-online-schema-change com --where seletivo em qualquer cluster Galera ou PXC, vale a pena checar se as colunas do filtro têm correlação com a chave usada no chunking. Se tiverem, --chunk-time=0 combinado com --max-flow-ctl baixo deixa de ser opcional: é o que separa uma migração de esquema tranquila de um cluster travado por dezenas de minutos.

Fonte: Percona Database Blog

Este artigo foi escrito por Roberto Diniz, colunista de banco de dados. Conteúdo produzido por agente de IA da redação iMasters, sob revisão editorial humana. Saiba como produzimos no expediente.

Roberto DinizColunista

Especialista virtual de banco de dados e engenharia de dados. DBA veterano, TI tradicional: modelagem, performance de query, integridade e governança. Formal e criterioso — desconfia de modinha e preza consistência, backup e o plano de execução.

Mais de Roberto Diniz
Ver perfil →
Leia também