O Planejador de Consultas
O planejador de consultas do PostgreSQL, às vezes chamado de otimizador, é o componente que decide como uma consulta será realmente executada antes que uma única linha seja lida.
Busque em todas as páginas da documentação
O planejador de consultas do PostgreSQL, às vezes chamado de otimizador, é o componente que decide como uma consulta será realmente executada antes que uma única linha seja lida.
Ele não segue um conjunto fixo de regras como um interpretador ingênuo faria; em vez disso, ele estima o custo de muitas estratégias de execução candidatas e escolhe a que ele acredita ser a mais barata, razão pela qual o PostgreSQL é descrito como um otimizador baseado em custo.
Tudo o que este planejador decide, desde o tipo de varredura até a ordem de junção e o algoritmo de junção, em última análise remonta a uma única entrada: quantas linhas ele espera em cada etapa, e essa expectativa vem de estatísticas coletadas sobre seus dados.
pg_stats, ANALYZE, GUCs do planejador.work_mem ou constantes de custo, ou diagnosticar má estimação sistemática em muitas consultas.EXPLAIN, design de índices, autovacuum e autoanalyze, estatísticas estendidas.Toda consulta que o planejador considera começa com uma pergunta: quantas linhas sobreviverão a este filtro, esta junção, esta agregação?
Esse número é chamado de estimativa de cardinalidade e é o valor mais consequente que o planejador produz, pois todos os cálculos de custo subsequentes dependem dele.
As estimativas de cardinalidade vêm da seletividade, a fração das linhas de uma tabela que se espera que correspondam a uma condição, que o planejador computa usando estatísticas armazenadas na view de sistema pg_stats.
Essas estatísticas não são derivadas varrendo a tabela inteira a cada consulta; elas são capturadas periodicamente pelo comando ANALYZE, que amostra um subconjunto de linhas e constrói um resumo estatístico compacto.
Esse resumo normalmente inclui uma lista dos valores mais comuns e suas frequências, um histograma da distribuição dos valores restantes, uma contagem estimada de valores distintos e a fração de linhas que são nulas.
Para um filtro de igualdade como status = 'shipped', o planejador verifica se 'shipped' aparece na lista de valores mais comuns e usa sua frequência registrada diretamente, se disponível, ou recorre a uma estimativa genérica baseada na contagem de valores distintos, se não.
Para um filtro de intervalo como created_at > now() - interval '7 days', o planejador consulta o histograma para estimar qual fração de linhas cai acima desse limite.
Uma vez que o planejador tem uma estimativa de linha para uma varredura de tabela base, ele alimenta esse número em uma fórmula de custo que leva em conta leituras de página e trabalho de CPU por linha, e esse custo resultante é o que você vê impresso em cada nó na saída de EXPLAIN.
O planejador não é onisciente sobre a intenção da sua consulta; ele só é tão bom quanto o modelo estatístico que lhe é fornecido, e esse modelo é uma aproximação deliberadamente compacta, não uma cópia completa dos seus dados.
A verdadeira complexidade do planejador emerge quando uma consulta combina múltiplas condições ou múltiplas tabelas, pois as seletividades precisam ser combinadas em uma única estimativa conjunta.
A suposição padrão é a independência de colunas: se status = 'shipped' corresponde a 5% das linhas e country = 'US' corresponde a 20% das linhas, o planejador assume que sua combinação corresponde a aproximadamente 1% das linhas por simples multiplicação.
Essa suposição de independência muitas vezes está errada em esquemas reais, onde colunas como country e currency, ou status e shipped_at IS NULL, são naturalmente correlacionadas, e multiplicar suas seletividades subestima ou superestima a verdadeira seletividade conjunta.
Estatísticas estendidas, criadas com CREATE STATISTICS, existem especificamente para corrigir isso, registrando correlação real observada, dependência ou contagens conjuntas de valores distintos entre colunas nomeadas.
Para junções, o planejador estende a mesma abordagem de estimar-then-cost para cada ordem de junção e algoritmo de junção viáveis que ele está disposto a considerar, usando programação dinâmica para evitar a força bruta de todas as permutações quando o número de tabelas cresce.
Ele pondera uma junção de loop aninhado, que repete uma varredura interna para cada linha externa, contra uma junção de hash, que constrói uma tabela de hash em memória uma vez, contra uma junção de merge, que requer que ambos os lados sejam pré-ordenados pela chave de junção, e cada uma tem uma forma de custo diferente dependendo das contagens de linha estimadas em cada lado.
É precisamente aqui que uma estimativa de cardinalidade ruim se torna perigosa em vez de meramente inconveniente: uma contagem de linha subestimada pode levar o planejador a escolher um loop aninhado que ele acredita que iterará apenas algumas vezes, quando na realidade ele itera centenas de milhares.
Vários parâmetros de configuração, coletivamente chamados de GUCs do planejador, moldam essa decisão sem tocar nas estatísticas em si; random_page_cost ajusta o quão cara a E/S aleatória parece em relação à E/S sequencial, e work_mem limita quanta memória uma operação de ordenação ou hash pode usar antes de despejar em disco.
O worker de autoanalyze do Autovacuum mantém as estatísticas atualizadas automaticamente, acionando um ANALYZE fresco assim que linhas suficientes em uma tabela mudam, embora cargas em massa e exclusões em massa possam ultrapassar esse limite e deixar o planejador trabalhando com uma imagem desatualizada nesse ínterim.
O planejador também trata subconsultas, Common Table Expressions (CTEs) e views em grande parte in-lineando e re-otimizando-as em contexto, em vez de tratá-las como caixas pretas opacas, razão pela qual o plano de um CTE pode parecer diferente dependendo de como ele é referenciado em outro lugar na consulta.
-- A suposição de seletividade conjunta em miniatura: sem estatísticas estendidas,
-- o planejador multiplica essas duas seletividades como se fossem independentes
-- mesmo que os pedidos 'US' sejam desproporcionalmente 'shipped'.
CREATE STATISTICS orders_country_status_stats (dependencies)
ON country, status FROM orders;
ANALYZE orders;Em escala, o comportamento do planejador torna-se um problema de sistema, não apenas uma curiosidade por consulta, pois milhares de consultas por segundo dependem do mesmo modelo estatístico se mantendo atualizado.
Tabelas particionadas complicam ainda mais a estimativa, pois o planejador deve combinar decisões de poda de partição com estatísticas por partição, e uma partição com dados incomumente distorcidos pode dominar silenciosamente o custo de uma consulta que toca todo o conjunto particionado.
Índices multicolunados também interagem com as estimativas do planejador, pois um índice pode tornar um plano barato o suficiente para ser selecionado mesmo quando a estimativa de linha subjacente está apenas aproximadamente correta, desde que o índice evite uma varredura completa das linhas descartadas.
Declarações preparadas introduzem um modo de planejamento distinto: o PostgreSQL pode planejar genericamente, sem conhecimento dos valores reais dos parâmetros, após um número suficiente de execuções com um plano personalizado, trocando uma pequena quantidade de precisão por plano pela capacidade de pular o replanejamento a cada chamada.
Cargas de trabalho baseadas em extensões adicionam suas próprias peculiaridades estatísticas; os índices de vizinhos mais próximos aproximados do pgvector, por exemplo, dependem do planejador estimar corretamente o custo de uma varredura de índice em comparação com uma varredura sequencial para busca de similaridade, o que depende de estimativas de linha precisas tanto quanto as varreduras B-tree comuns.
Má estimação sistemática em todo um esquema, em vez de uma única consulta, geralmente é um sinal de um problema estrutural: uma tabela onde os limites do autoanalyze são muito grosseiros para seu volume de escrita, ou uma carga de trabalho construída inteiramente em predicados de expressão que estatísticas de coluna simples não conseguem descrever.
| Abordagem | Força | Fraqueza | Melhor Ajuste |
|---|---|---|---|
| Estatísticas padrão de coluna única | Configuração zero, automática via autoanalyze | Assume independência de colunas | A maioria das tabelas com colunas de filtro não relacionadas |
Maior default_statistics_target / SET STATISTICS por coluna | Detalhe de histograma e MCV mais fino | ANALYZE mais lento, linhas de catálogo maiores | Colunas fortemente distorcidas usadas em filtros frequentes |
Estatísticas estendidas (dependencies, ndistinct, mcv) | Modela correlação real de colunas | Deve ser explicitamente criado e reanalisado | Pares de colunas correlacionadas conhecidas causando estimativas conjuntas ruins |
Ajuste manual de GUCs do planejador (random_page_cost, work_mem) | Muda o modelo de custo para corresponder ao hardware real | Efeito global ou por função, não específico da consulta | Armazenamento baseado em SSD ou operações de ordenação/hash com restrição de memória |
pg_stats.ANALYZE amostra um número limitado de linhas controlado por default_statistics_target, não a tabela inteira, o que o mantém rápido mesmo em relações muito grandes.ANALYZE.random_page_cost corrigem estimativas ruins." - GUCs remodelam o modelo de custo aplicado a uma determinada estimativa de linha; eles não fazem nada para corrigir uma estimativa que está simplesmente errada.O planejador atribui um custo numérico a cada maneira candidata de executar uma consulta e escolhe a menor, em vez de seguir um conjunto fixo de regras.
Esse custo é derivado de estimativas de contagem de linhas e constantes de custo configuráveis, não de executar as alternativas.
Elas vêm de pg_stats, uma view de catálogo preenchida pelo comando ANALYZE.
ANALYZE amostra a tabela em vez de varrê-la completamente, então registra os valores mais comuns, um histograma, contagens de valores distintos e a fração de nulos por coluna.
Diferentes valores literais podem atingir partes diferentes das estatísticas de uma coluna, como um valor mais comum versus um bucket de histograma, produzindo diferentes estimativas de seletividade.
O planejamento genérico de prepared statements também pode divergir de um plano personalizado construído para um conjunto específico de parâmetros.
Ele estima o custo de cada algoritmo usando as contagens de linha estimadas em ambos os lados da junção e escolhe o mais barato.
Loops aninhados favorecem um lado externo pequeno, junções de hash favorecem um lado não ordenado, mas que caiba na memória, e junções de merge favorecem entradas que já estão ordenadas ou são facilmente ordenáveis.
Multiplicar seletividades para combinar múltiplas condições é matematicamente correto apenas quando as colunas são estatisticamente independentes.
Esquemas reais frequentemente têm colunas correlacionadas, que é exatamente a lacuna que as estatísticas estendidas foram projetadas para preencher.
O PostgreSQL começa a rastrear uma estatística conjunta, como dependência funcional ou contagem combinada de valores distintos, entre as colunas nomeadas.
Essa estatística conjunta é populada na próxima execução de ANALYZE e usada automaticamente sempre que o planejador estima a seletividade para essas colunas em conjunto.
Pode melhorar a precisão para colunas distorcidas, registrando mais valores mais comuns e buckets de histograma mais finos.
Também torna ANALYZE mais lento e aumenta o tamanho do catálogo, portanto, é melhor aplicá-lo seletivamente às colunas que realmente precisam dele.
O worker de autoanalyze do Autovacuum aciona um ANALYZE fresco assim que a contagem estimada de alterações de uma tabela cruza um limite.
Cargas em massa ou exclusões em massa podem ultrapassar esse limite, razão pela qual um ANALYZE manual após uma grande alteração de dados muitas vezes vale a pena ser feito explicitamente.
EXPLAIN é a ferramenta que expõe o plano escolhido pelo planejador e seus custos subjacentes e estimativas de linha para inspeção.
O próprio planejador é executado em cada consulta, independentemente de você olhar ou não para EXPLAIN.
Eles podem mudar a preferência relativa do modelo de custo entre E/S sequencial e aleatória, o que às vezes influencia uma decisão limite.
Eles não podem corrigir uma estimativa de linha fundamentalmente errada, que é um problema de estatísticas em vez de um problema de constantes de custo.
O planejador geralmente in-linea e re-otimiza subconsultas e CTEs no contexto da consulta circundante, em vez de tratá-las como caixas pretas fixas.
Isso significa que o mesmo texto de CTE pode ser planejado de forma diferente dependendo de quais predicados e junções o cercam.
Não, o planejador decide qual plano executar, e o executor é o componente separado que realmente percorre esse plano e produz linhas.
EXPLAIN sozinho exercita apenas o planejador; EXPLAIN ANALYZE exercita ambos.
pg_stats e a mecânica de histograma referenciados aquiVersões do Stack: Esta página foi escrita para PostgreSQL 18.4 (linha estável 18, linha de manutenção 17); a arquitetura do planejador baseado em custo e o modelo
pg_statsdescritos aqui são estáveis em versões principais recentes.
Revisado por Chris St. John·Última atualização: 19 de jul. de 2026