Í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.
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 informada | Próxima evidência a examinar | Limite da interpretação |
|---|---|---|
waiting for writers before build | Contagens de bloqueadores, bloqueador atual, responsável pela transação | Não representa uma porcentagem da varredura da tabela |
building index e suas subfases | Contadores de blocos ou tuplas em observações repetidas, telemetria de recursos | As informações disponíveis dependem do método de acesso |
waiting for writers before validation | Processos de gravação relevantes e suas transações | Uma espera posterior de coordenação, que não indica necessariamente falha no trabalho |
index validation: scanning index, sorting tuples ou scanning table | Nome da fase e contadores disponíveis | As varreduras do índice e da tabela medem trabalhos diferentes |
waiting for old snapshots | Bloqueador atual e atividade relacionada a instantâneos | Investigue 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.
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 confirmado | Próxima ação aprovada | Verificação obrigatória |
|---|---|---|
| Criação ativa com progresso esperado | Observar | Comparar evidências repetidas levando em conta a fase |
| Criação ativa em espera | Investigar e coordenar a resolução | Confirmar a transação relevante e seu responsável |
| Falha na criação de um índice independente, com a definição pretendida | Corrigir a causa e, depois, remover e recriar ou realizar uma reindexação concorrente no nível do índice, se elegível | Confirmar que a criação terminou e que a recuperação está autorizada |
| Índice válido, com definição divergente | Planejar uma migração separada | Não tratar o nome existente como critério de aceitação |
| Artefatos de uma reindexação concorrente que falhou | Seguir o caminho documentado específico para o artefato | Examinar 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ção | Momento de coleta | Decisão apoiada |
|---|---|---|
| Fase e contadores aplicáveis | Observações repetidas durante a criação | Trabalho em progresso ou necessidade de investigação |
| PID do bloqueador, contexto da transação, eventos de espera | Durante uma espera | Resolução coordenada com o responsável |
| Resultado do comando, definição, indicadores do catálogo | Antes da recuperação e após a conclusão | Pós-condição pretendida alcançada ou não |
| CPU, E/S e latência e erros da aplicação | Antes, durante e após a operação | Impacto 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ção | Evidência guardada |
|---|---|
| Registrar a intenção | Esquema, tabela, definição completa do índice |
| Configurar a execução | DDL concorrente fora de transações que a englobem |
| Preservar a falha | Erro original e resultado do comando |
| Examinar antes de tentar novamente | Fase, processo de servidor, catálogo, dependências |
| Aprovar a recuperação | Responsável identificado e caminho elegível |
| Aceitar a conclusão | Comando 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
- https://www.postgresql.org/docs/current/sql-createindex.html
- https://www.postgresql.org/docs/current/progress-reporting.html
- https://www.postgresql.org/docs/current/catalog-pg-index.html
- https://www.postgresql.org/docs/current/sql-dropindex.html
- https://www.postgresql.org/docs/current/sql-reindex.html
- https://www.postgresql.org/docs/current/monitoring-stats.html
- https://www.postgresql.org/docs/current/functions-info.html
- https://github.com/rails/rails/issues/52583
- https://www.postgresql.org/docs/current/catalog-pg-constraint.html
- https://www.postgresql.org/docs/current/catalog-pg-class.html
- https://www.postgresql.org/docs/current/catalog-pg-namespace.html
- https://pganalyze.com/blog/postgres-create-index
Escrito por
Hamza DiazHamza 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.
