← Voltar ao Blog
Backend & Architecture

Índices concorrentes no PostgreSQL: esperas e recuperação de falhas na criação

Um comando de criação concorrente de índice em espera e um índice inválido deixado após uma falha exigem respostas diferentes. Use diagnósticos que considerem a fase, verificações do catálogo e o guia de recuperação PACE para escolher uma intervenção e verificar o estado pretendido da migração.

Escrito por Hamza Diaz
27 de outubro de 202610 min de leitura16 visualizações

Considere uma tentativa hipotética de repetir uma migração que informa a existência de uma relação. A migração prossegue, mas ninguém verificou se o índice pode ser utilizado. O PostgreSQL pode deixar um índice inválido após uma falha na criação concorrente; IF NOT EXISTS não valida a definição existente. A documentação do comando explica ambos os comportamentos.

Comece pelo processo de servidor. Ele ainda está criando o índice ou está em espera? Determine se a criação terminou antes de considerar a recuperação. Depois, compare o índice real com a migração pretendida. Um aviso de que o nome existe é uma verificação de conclusão insuficiente.

Este guia abrange tabelas comuns de aplicações, sem particionamento. Os modelos SQL foram revisados com base nas fontes, mas não foram executados. Adapte os nomes hipotéticos de public.orders e verifique o banco de dados e os privilégios necessários. Consulte a documentação da versão principal em uso e ensaie as alterações em um ambiente isolado.

O que CREATE INDEX CONCURRENTLY muda e o que permanece igual

Compatibilidade com gravações não significa ausência de bloqueios ou esperas

O CREATE INDEX comum bloqueia gravações na tabela, mas permite leituras comuns. CREATE INDEX CONCURRENTLY permite gravações normais durante a criação, mas ainda exige coordenação e espera. Apenas uma criação concorrente pode ser executada por vez em uma determinada tabela. Ela deve ser executada fora de um bloco de transação. O PostgreSQL documenta essas restrições.

Duas varreduras, esperas por transações e a transição de validade

A criação concorrente registra uma entrada inválida no catálogo antes de realizar duas varreduras da tabela em transações separadas. Ela espera pelas transações de modificação relevantes antes das varreduras e pelos instantâneos mais antigos após a segunda varredura. Essa entrada inválida pode pertencer a uma criação que está funcionando normalmente. Não a altere até saber que esse não é o caso. As varreduras e esperas adicionais também significam que a criação concorrente pode demorar mais. Veja a sequência de criação.

Diagnostique a espera na criação de índices do PostgreSQL por fase e processo de servidor

Consulte pg_stat_progress_create_index antes de estimar o progresso

Conecte-se ao banco de dados de destino. Este modelo somente de leitura filtra por tabela, mantendo os identificadores brutos e convertendo identificadores indisponíveis com valor zero em nulos:

SELECT p.pid, p.command, p.phase, p.relid,
       NULLIF(p.index_relid, 0) AS index_relid,
       p.lockers_total, p.lockers_done,
       NULLIF(p.current_locker_pid, 0) AS current_locker_pid,
       p.blocks_total, p.blocks_done
FROM pg_stat_progress_create_index AS p
WHERE p.datname = current_database()
  AND p.relid = to_regclass('public.orders');

Um objeto de destino inexistente é resolvido como nulo e não produz correspondência. Verifique o identificador antes de interpretar um resultado vazio. Consulte a documentação de to_regclass.

Interprete os contadores no contexto da fase informada. As definições de progresso do PostgreSQL distinguem os seguintes trabalhos.

Fase informadaPróxima evidência a examinarLimite da interpretação
waiting for writers before buildContagens de bloqueadores, bloqueador atual, responsável pela transaçãoNão representa uma porcentagem da varredura da tabela
building index e suas subfasesContadores de blocos ou tuplas em observações repetidas, telemetria de recursosAs informações disponíveis dependem do método de acesso
waiting for writers before validationProcessos de gravação relevantes e suas transaçõesUma espera posterior de coordenação, que não indica necessariamente falha no trabalho
index validation: scanning index, sorting tuples ou scanning tableNome da fase e contadores disponíveisAs varreduras do índice e da tabela medem trabalhos diferentes
waiting for old snapshotsBloqueador atual e atividade relacionada a instantâneosInvestigue o contexto da transação antes de intervir

Na linha de validação acima, "sorting tuples" e "scanning table" também têm o prefixo completo de fase "index validation:". A reindexação concorrente pode informar "waiting for readers before marking dead" e "waiting for readers before dropping"; a mesma referência de progresso descreve essas esperas.

Os totais de blocos descrevem a fase atual. Eles não permitem determinar o progresso de ponta a ponta, e totais iguais a zero ou indisponíveis não sustentam uma estimativa percentual.

Correlacione o processo de servidor em espera com evidências de transações e bloqueios

Examine os processos de servidor de criação junto com seus bloqueadores atuais informados, usando este modelo separado somente de leitura.

WITH progress AS (
  SELECT pid, current_locker_pid
  FROM pg_stat_progress_create_index
  WHERE datname = current_database()
    AND relid = to_regclass('public.orders')
), participants AS (
  SELECT pid FROM progress
  UNION
  SELECT current_locker_pid FROM progress
  WHERE current_locker_pid > 0
)
SELECT a.pid, a.state, a.xact_start, a.backend_xmin,
       a.wait_event_type, a.wait_event,
       pg_blocking_pids(a.pid) AS lock_blocking_pids
FROM pg_stat_activity AS a
JOIN participants AS t ON t.pid = a.pid;

pg_blocking_pids informa os processos que causam bloqueios, mas não explica todos os atrasos relacionados a instantâneos. Um vetor vazio não descarta uma espera. Da mesma forma, um xact_start ou backend_xmin antigo justifica uma investigação; não identifica uma sessão a encerrar. Coordene com o responsável pela transação. Esta consulta inclui o processo de servidor de criação e seu bloqueador atual informado, sem formar uma árvore recursiva de bloqueadores. Investigue separadamente os PIDs dos bloqueadores retornados. A função pode retornar zero para um bloqueador que seja uma transação preparada; zero não é um processo de servidor a encerrar. Consulte a mesma referência da função.

Para uma comparação fora do PostgreSQL, o guia de observabilidade da criação de motores TensorRT da Optijara examina como distinguir um trabalho demorado de uma falha.

Interprete com cautela a ausência de linhas e a visibilidade parcial

A ausência de uma linha de progresso não permite concluir nada. Verifique o PID do processo de servidor que foi registrado e o resultado no cliente; depois, correlacione os erros do servidor com o estado do catálogo. Confirme a seleção do banco de dados e as permissões do papel de monitoramento. Os detalhes de outras sessões podem estar restritos. Transações longas de monitoramento também podem deixar as observações desatualizadas; repita-as fora dessa transação. A documentação de monitoramento de atividade explica ambas as limitações.

Examine os índices inválidos antes de tentar novamente com IF NOT EXISTS

Verifique indisvalid, indisready, indislive e a definição real

Examine o índice indicado pelo nome e a tabela à qual ele realmente pertence. Um registro de migrações não permite determinar nenhum dos dois. As junções abaixo seguem a documentação do catálogo de relações e do catálogo de espaços de nomes.

SELECT ns.nspname AS index_schema, ix.relname AS index_name,
       tn.nspname AS table_schema, t.relname AS table_name,
       i.indisunique, i.indisvalid, i.indisready, i.indislive,
       pg_get_indexdef(i.indexrelid) AS definition
FROM pg_index AS i
JOIN pg_class AS ix ON ix.oid = i.indexrelid
JOIN pg_namespace AS ns ON ns.oid = ix.relnamespace
JOIN pg_class AS t ON t.oid = i.indrelid
JOIN pg_namespace AS tn ON tn.oid = t.relnamespace
WHERE ns.nspname = 'public'
  AND ix.relname = 'orders_customer_id_idx';

Compare a definição com a migração aprovada, incluindo a unicidade e o método de acesso. Verifique a ordem das chaves, as expressões, as colunas incluídas e o predicado. pg_get_indexdef reconstrói essa definição.

Em pg_index, indisvalid descreve a validade para consultas. indisready indica a prontidão para a manutenção em inserções e atualizações; indislive registra se o índice permanece ativo. Um valor falso no indicador de atividade significa que o índice está sendo removido e deve ser ignorado para todos os fins. Um índice inválido ainda pode gerar sobrecarga nas gravações. O indicador de invalidez, isoladamente, não identifica uma falha na criação concorrente.

A criação de índices únicos exige cuidado especial. A imposição de unicidade sobre outras transações começa quando a segunda varredura da tabela se inicia, antes de o índice se tornar válido. Uma falha durante essa varredura pode deixar um índice inválido que ainda rejeita gravações duplicadas. O indicador de invalidez também não pode certificar que os dados existentes atendem à unicidade. As ressalvas da criação concorrente explicam essa sequência.

Por que a idempotência baseada no nome não equivale a uma migração bem-sucedida

IF NOT EXISTS suprime um erro quando já existe uma relação com o mesmo nome. Ele não reconstrói um índice inválido nem garante uma definição equivalente. O relato #52583 do Rails relata o problema resultante de tempo limite e nova tentativa. Esse relato público não comprova a existência de uma correção atual no Rails. A referência do comando do PostgreSQL define o comportamento subjacente.

O guia de recuperação PACE: fase, avaliação, escolha e estabelecimento da prontidão

Fase e avaliação: registre as evidências antes de alterar o estado

PACE é uma estrutura editorial apresentada aqui. Antes de alterar o estado, salve o erro original e a fase atual. Guarde a identidade e a definição do índice, junto com o contexto do responsável pela transação.

flowchart TD P[Fase: identificar trabalho ativo e esperas] --> A[Avaliação: examinar identidade e evidências de falha] A --> C{Escolher um caminho aprovado} C --> W[Observar ou coordenar o trabalho ativo] C --> R[Corrigir a causa e recuperar o índice elegível] W --> E[Estabelecer prontidão: verificações do comando e do catálogo] R --> E

Antes da remoção, verifique se alguma restrição utiliza o índice indicado pelo nome.

SELECT conname, contype, conrelid::regclass AS owning_table
FROM pg_constraint
WHERE conindid = to_regclass('public.orders_customer_id_idx');

pg_constraint.conindid identifica os índices que dão suporte às restrições. Um resultado vazio ainda exige investigar outras dependências e a titularidade. Adicionar CASCADE não substitui essa revisão.

Escolha: esperar, remover e recriar ou reindexar

Escolha um caminho com base no estado confirmado. Um ciclo incondicional de novas tentativas ignora essa decisão.

Estado confirmadoPróxima ação aprovadaVerificação obrigatória
Criação ativa com progresso esperadoObservarComparar evidências repetidas levando em conta a fase
Criação ativa em esperaInvestigar e coordenar a resoluçãoConfirmar a transação relevante e seu responsável
Falha na criação de um índice independente, com a definição pretendidaCorrigir a causa e, depois, remover e recriar ou realizar uma reindexação concorrente no nível do índice, se elegívelConfirmar que a criação terminou e que a recuperação está autorizada
Índice válido, com definição divergentePlanejar uma migração separadaNão tratar o nome existente como critério de aceitação
Artefatos de uma reindexação concorrente que falhouSeguir o caminho documentado específico para o artefatoExaminar a identidade, as dependências e o índice válido remanescente

Use o modelo de remoção e recriação a seguir, que não foi executado, somente depois de a criação que falhou ter terminado e de as verificações de dependências permitirem a remoção. Ele pressupõe que a definição aprovada seja exatamente um índice de árvore B não único em public.orders(customer_id).

DROP INDEX CONCURRENTLY public.orders_customer_id_idx;
CREATE INDEX CONCURRENTLY orders_customer_id_idx
  ON public.orders USING btree (customer_id);

Envie comandos separados de nível superior fora de blocos de transação. Se a remoção falhar, pare e examine novamente. CREATE INDEX não aceita um nome de índice qualificado pelo esquema; qualificar public.orders coloca o índice nesse esquema. Verifique as restrições distintas de criação e remoção concorrente.

Para um índice inválido elegível cuja definição deva ser preservada, o caminho alternativo está abaixo.

REINDEX INDEX CONCURRENTLY public.orders_customer_id_idx;

Resolva a causa primeiro. A reindexação não pode corrigir dados duplicados nem alterar uma definição incorreta, e não elimina a carga de trabalho que causa o bloqueio. Indique o índice explicitamente: somente a forma no nível do índice pode realizar a reconstrução concorrente de um índice inválido. Não a substitua por uma reindexação concorrente de toda a tabela. Verifique o processo e as exclusões.

Uma reindexação concorrente que falha pode deixar uma substituição inválida _ccnew, que o caminho documentado remove antes de tentar novamente. Já um _ccold inválido pode ser a cópia antiga deixada após uma reconstrução bem-sucedida. Sufixos numéricos são possíveis. Verifique o índice válido remanescente e as dependências antes de aprovar a remoção. Nunca remova índices em massa com base no sufixo. O PostgreSQL documenta os diferentes caminhos de limpeza.

Estabelecimento da prontidão: verifique as pós-condições pretendidas

Após a recuperação, exija que o comando seja bem-sucedido e repita a consulta ao catálogo. Confira se o esquema e a tabela correspondem à definição pretendida; confirme os indicadores de validade, prontidão e atividade. A validade não obriga o planejador a selecionar o índice, e transações mais antigas podem atrasar a disponibilidade do índice para uso. O guia de decisões sobre sondas do Kubernetes da Optijara explora a diferença mais ampla entre a saúde de um processo e sua prontidão.

Mantenha um registro de observações em vez de inventar um limite universal de tempo decorrido.

MediçãoMomento de coletaDecisão apoiada
Fase e contadores aplicáveisObservações repetidas durante a criaçãoTrabalho em progresso ou necessidade de investigação
PID do bloqueador, contexto da transação, eventos de esperaDurante uma esperaResolução coordenada com o responsável
Resultado do comando, definição, indicadores do catálogoAntes da recuperação e após a conclusãoPós-condição pretendida alcançada ou não
CPU, E/S e latência e erros da aplicaçãoAntes, durante e após a operaçãoImpacto na carga de trabalho que exige uma resposta operacional

Essas são medições propostas, não resultados de testes de desempenho. O JSON abaixo resume a estrutura e a regra de conclusão.

{
  "framework": "pace",
  "layers": ["phase", "assess", "choose", "establish_readiness"],
  "completionRequires": ["successful_command", "intended_definition", "valid_ready_live"],
  "automaticDestructiveRetry": false
}

Erros das equipes ao repetir migrações

Confundir um aviso de nova tentativa bem-sucedida com um índice válido

Um aviso de nova tentativa responde a uma pergunta mais restrita do que aquela que a migração precisa responder. Verifique o catálogo e a definição separadamente. Mantenha essa verificação de aceitação nos fluxos de trabalho de CI/CD assistidos por IA, incluindo migrações propostas por automação.

Tentar novamente antes de corrigir a causa da falha na criação

Outra tentativa não resolve dados duplicados nem uma transação aberta. Guarde o erro inicial para que a recuperação trate a falha que você realmente observou. Use a lista de verificação a seguir antes de prosseguir.

Ponto de verificaçãoEvidência guardada
Registrar a intençãoEsquema, tabela, definição completa do índice
Configurar a execuçãoDDL concorrente fora de transações que a englobem
Preservar a falhaErro original e resultado do comando
Examinar antes de tentar novamenteFase, processo de servidor, catálogo, dependências
Aprovar a recuperaçãoResponsável identificado e caminho elegível
Aceitar a conclusãoComando bem-sucedido e índice correspondente válido, pronto e ativo

Tratar a limpeza destrutiva como idempotência genérica

Mantenha a limpeza destrutiva fora da lógica genérica de novas tentativas. Exija aprovação humana e uma nova verificação do estado antes de agir sobre sessões ou índices inválidos. As referências de criação concorrente e reindexação explicam por que nomes e indicadores intermediários não são fundamentos suficientes para a remoção.

Ressalvas: partições, restrições, acesso ao monitoramento e custo operacional

Onde termina o guia para tabelas comuns

A documentação citada não oferece suporte à criação concorrente na tabela pai particionada. Crie os índices das partições folha de forma concorrente e, depois, planeje separadamente a montagem do índice da tabela pai. Não presuma que essa operação na tabela pai seja livre de bloqueios. O PostgreSQL descreve esse caminho.

DROP INDEX CONCURRENTLY aceita um índice e proíbe CASCADE. Ele deve ser executado fora de um bloco de transação. Exclui índices de tabelas particionadas e a remoção direta de índices que dão suporte a restrições UNIQUE ou PRIMARY KEY. Verifique a elegibilidade para remoção. A reindexação concorrente exclui catálogos do sistema e índices de restrições de exclusão. Suas verificações de elegibilidade são separadas.

Por que a concorrência envolve concessões e não garante desempenho

As varreduras adicionais consomem CPU e E/S. Reserve tempo de engenharia para ensaios e monitoramento; depois, defina uma janela de observação e um plano de resposta para a carga de trabalho. A documentação de criação concorrente alerta sobre a carga operacional.

Proteja o texto das consultas e os detalhes sensíveis de atividade ao compartilhar evidências de incidentes. Considere as restrições de acesso ao monitoramento. Esses modelos não foram executados em um ambiente de teste PostgreSQL nesta revisão. Nenhuma espera ou recuperação foi observada, e não se alega qualquer resultado de produção ou de testes de desempenho. Ensaie o caminho selecionado antes do uso operacional.

Pontos principais

  • 1Leia a fase de criação informada antes de tratar o tempo decorrido ou os contadores de blocos como progresso.
  • 2Um índice inválido pode pertencer a uma criação ativa; confirme a falha antes de autorizar a limpeza.
  • 3IF NOT EXISTS verifica a existência do nome, não a equivalência da definição nem a validade do índice.
  • 4Use PACE para conectar as evidências da fase, a avaliação do catálogo, a recuperação aprovada e as verificações de prontidão.
  • 5A reindexação concorrente é uma opção para índices elegíveis, não uma correção para dados duplicados ou uma definição errada.
  • 6Verifique a conclusão do comando e o índice pretendido, válido, pronto e ativo, respeitando as ressalvas de visibilidade das transações.

Conclusão

Comece pela fase. Se a criação falhou, determine o que restou e corrija a causa antes de escolher um caminho de recuperação aprovado. Um aviso de nova tentativa não pode certificar o índice. Exija que o comando seja bem-sucedido e verifique a definição pretendida e o estado válido, pronto e ativo, levando em conta os limites documentados de visibilidade das transações. Se sua equipe quiser uma segunda revisão das salvaguardas de migração do banco de dados, a Optijara pode ajudar a avaliar o plano.

Perguntas frequentes

Por que CREATE INDEX CONCURRENTLY está em espera? Ele evita todos os bloqueios?

Ele permite gravações normais na tabela, mas ainda se coordena com outras transações. Examine `pg_stat_progress_create_index.phase`, o PID do processo de criação e o bloqueador atual informado. Um resultado vazio de `pg_blocking_pids` não descarta todas as esperas. Consulte os [relatórios de progresso](https://www.postgresql.org/docs/current/progress-reporting.html) e as [funções de identificação de bloqueadores](https://www.postgresql.org/docs/current/functions-info.html).

Como distinguir uma criação ativa de um índice inválido cuja criação falhou?

Correlacione o progresso, a atividade do processo de servidor, os erros do comando e o estado do catálogo. O estado inválido ocorre durante a criação, portanto `indisvalid=false`, isoladamente, não comprova uma falha nem autoriza a remoção. Se não houver progresso informado, verifique a seleção do banco de dados, as permissões e o resultado do processo de servidor. Consulte o [comportamento da criação concorrente](https://www.postgresql.org/docs/current/sql-createindex.html) e a [visibilidade do monitoramento](https://www.postgresql.org/docs/current/monitoring-stats.html).

IF NOT EXISTS reconstrói um índice inválido do PostgreSQL?

Não. Ele suprime um erro quando já existe uma relação com o mesmo nome, sem validar a equivalência nem reconstruir um índice inválido. Examine a definição e o estado do catálogo, corrija a causa da falha e escolha um caminho de recuperação aprovado. Consulte a [referência de CREATE INDEX](https://www.postgresql.org/docs/current/sql-createindex.html).

Posso recuperar um índice inválido sem removê-lo primeiro?

Sim, `REINDEX INDEX CONCURRENTLY` pode reconstruir um índice inválido elegível com a definição pretendida. Ele não pode corrigir dados duplicados, uma definição errada nem a carga de trabalho que provoca a espera. Resolva a causa, verifique as restrições e confirme as pós-condições. Consulte [REINDEX](https://www.postgresql.org/docs/current/sql-reindex.html); [remover e recriar](https://www.postgresql.org/docs/current/sql-createindex.html) é outra opção documentada.

Um índice único inválido ainda pode rejeitar gravações duplicadas?

Sim. A imposição de unicidade sobre outras transações começa quando a segunda varredura da tabela se inicia, antes de o índice se tornar válido. Se a criação concorrente de um índice único falhar durante a segunda varredura, o índice inválido continuará impondo unicidade depois disso. O estado inválido não certifica a ausência de efeitos prejudiciais nem a unicidade completa dos dados. Consulte as [ressalvas da criação concorrente de índices únicos](https://www.postgresql.org/docs/current/sql-createindex.html#SQL-CREATEINDEX-CONCURRENTLY).

Fontes

Compartilhar este artigo

Hamza Diaz

Escrito por

Hamza Diaz

Hamza Diaz é o fundador da Optijara, onde cria agentes de IA práticos, sistemas de automação e fluxos de trabalho do Copilot para empresas de serviços. Ele escreve sobre operações de IA, estratégia de agentes e implementação no mundo real para equipes que querem sistemas úteis em vez de exagero.