Arquitetura PostgreSQL
Como o PostgreSQL funciona por dentro, do básico ao avançado: processos, armazenamento físico, MVCC, WAL, replicação, segurança e extensibilidade. Este é o mapa conceitual que sustenta os dois estudos aplicados do hub — otimização de queries e migração sem downtime — que aprofundam, respectivamente, a Fase 9 deste guia.
Fundamentos e Processos
Filosofia do projeto e o que roda por trás de uma conexão
O PostgreSQL nasceu do projeto acadêmico POSTGRES (Berkeley, 1986), pensado desde o início para ser extensível — não só nos dados que armazena, mas no próprio banco: tipos, operadores, índices e linguagens procedurais podem ser adicionados sem recompilar o servidor.
Diferente de bancos que usam um modelo multi-thread, o Postgres usa um processo do sistema operacional por conexão — uma decisão de projeto antiga que ainda molda como ele escala e por que connection pooling (Fase 9) é praticamente obrigatório em produção.
fork() de um novo processo backend dedicado. Em paralelo rodam processos auxiliares fixos: checkpointer (grava páginas sujas periodicamente), background writer (grava páginas sujas de forma incremental fora dos checkpoints), WAL writer (descarrega o log de escrita para disco), autovacuum launcher (dispara workers de limpeza — Fase 5) e stats collector (coleta estatísticas de uso). Cada backend tem sua própria memória privada, mas todos compartilham uma região de memória compartilhada (shared_buffers) via mmap/shmget.
PGDATA é o diretório raiz de um cluster Postgres (o termo "cluster" aqui significa
"instância do servidor com seus múltiplos bancos", não um cluster distribuído). Sua estrutura interna
raramente precisa ser tocada à mão, mas entendê-la ajuda a diagnosticar problemas de disco e I/O.
PGDATA: base/ guarda um subdiretório por banco de dados, cada um com os arquivos de tabela (um oid numérico por relação, fatiado a cada 1GB); pg_wal/ guarda os segmentos do write-ahead log (Fase 4); pg_xact/ guarda o status de commit de cada transação; global/ guarda catálogos compartilhados entre todos os bancos. Três arquivos de configuração controlam o comportamento do servidor: postgresql.conf (parâmetros gerais — memória, WAL, planner), pg_hba.conf (regras de autenticação — Fase 7) e pg_ident.conf (mapeamento de identidade para autenticação externa).Todo metadado do Postgres — tabelas, colunas, índices, roles, tipos — é guardado em tabelas comuns, consultáveis com SQL normal. Não existe um "modo especial" para ver a estrutura do banco.
pg_catalog é o esquema nativo do Postgres, com tabelas como pg_class (relações: tabelas, índices, views), pg_attribute (colunas) e pg_proc (funções) — rápido, mas com nomes internos pouco amigáveis. information_schema é uma camada padronizada pelo SQL:2016 sobre esses mesmos dados, portável entre SGBDs diferentes, mas mais lenta e menos completa para peculiaridades específicas do Postgres. Na prática: use information_schema para queries portáveis entre bancos, e pg_catalog quando precisar de detalhes internos (como o plano de um índice ou o OID de uma relação).SELECT relname, relkind, reltuples FROM pg_catalog.pg_class WHERE relnamespace = 'public'::regnamespace ORDER BY reltuples DESC; -- relkind: 'r' tabela, 'i' índice, 'v' view, 'm' matview
Armazenamento Físico
Como uma linha vira bytes em disco
Uma tabela no Postgres é, fisicamente, um arquivo (ou vários, fatiados a cada 1GB) dividido em páginas de 8KB — o tamanho padrão de leitura e escrita para praticamente qualquer operação de disco.
xmin/xmax (o ID da transação que a criou/removeu), então um UPDATE no Postgres nunca sobrescreve uma tupla no lugar — ele cria uma nova versão e marca a antiga como morta. O conjunto de todas as páginas de uma tabela é chamado de heap: não há ordem física garantida entre as tuplas, ao contrário de um índice clustered em outros SGBDs.
Por padrão, todo objeto de um banco fica sob o mesmo PGDATA. Tablespaces permitem apontar
tabelas ou índices específicos para outro caminho de disco — útil para separar dados quentes e frios
entre discos de velocidades diferentes.
PGDATA. Casos de uso reais: mover um índice muito acessado para um SSD NVMe enquanto a tabela histórica fica num disco mais barato; isolar uma tabela de logs de alto volume de escrita do resto do banco para não competir por I/O nos checkpoints; ou, em ambientes on-premise antigos, contornar limites de tamanho de um único volume. Em ambientes cloud-managed (RDS, Cloud SQL), tablespaces raramente são usados, já que o storage já é elástico por padrão.CREATE TABLESPACE fast_ssd LOCATION '/mnt/nvme/pgdata_idx'; ALTER INDEX idx_pedidos_status SET TABLESPACE fast_ssd;
Como toda tupla precisa caber numa página de 8KB, campos grandes — um JSONB extenso, um texto longo — não podem simplesmente crescer. O TOAST (The Oversized-Attribute Storage Technique) resolve isso.
text, bytea, jsonb) são movidos para uma tabela TOAST auxiliar associada à tabela original, e o campo original passa a guardar só um ponteiro. Antes de mover, o Postgres tenta comprimir o valor no lugar (estratégia PGLZ ou LZ4 a partir da v14); só se ainda não couber é que ele é fatiado em pedaços de ~2KB e movido para fora da linha (out-of-line storage). Isso explica por que uma tabela com poucas colunas JSONB grandes pode ter um arquivo .toast maior que a própria tabela.ALTER TABLE ... ALTER COLUMN ... SET STORAGE {PLAIN|EXTENDED|EXTERNAL|MAIN} — útil quando você sabe que um campo específico nunca deveria ser comprimido (dados já comprimidos, como imagens) ou nunca deveria sair da linha.Toda tabela vem acompanhada de dois arquivos auxiliares compactos que o planner e o executor consultam sem precisar tocar o heap inteiro: a FSM e a VM.
_fsm) rastreia, por página, quanto espaço livre existe — assim um INSERT não precisa varrer o heap inteiro procurando onde caber uma nova tupla. A Visibility Map (_vm) marca páginas onde todas as tuplas são visíveis para todas as transações em andamento (nenhuma tupla morta, nada em disputa) — isso permite dois ganhos: o VACUUM pode pular inteiramente essas páginas (Fase 5), e um Index Only Scan pode responder sem tocar o heap, só o índice, quando a página está marcada como "all-visible". Manter a VM atualizada é, por isso, um dos motivos práticos mais importantes para rodar autovacuum regularmente, não só a limpeza de tuplas mortas.MVCC e Concorrência
Como o Postgres deixa leitores e escritores trabalharem sem se bloquearem
Multiversion Concurrency Control é a razão pela qual um SELECT longo nunca trava um UPDATE concorrente no Postgres — cada transação enxerga uma "fotografia" consistente dos dados no momento em que começou.
xmin (a transação que a criou) e marca a versão antiga com xmax (a transação que a substituiu). Quando uma transação lê uma linha, ela compara os IDs xmin/xmax de cada versão contra seu próprio snapshot — um retrato de quais transações já haviam sido commitadas no instante em que ela começou — e escolhe a versão correta a enxergar. O resultado prático: leituras nunca precisam de lock, porque elas simplesmente ignoram versões que não deveriam ver, em vez de esperar por elas.O padrão SQL define quatro níveis de isolamento, mas o Postgres implementa só três deles fisicamente — porque o MVCC já elimina uma das anomalias na raiz.
READ UNCOMMITTED existe apenas como sinônimo de READ COMMITTED no Postgres, já que o MVCC torna "leitura suja" (ver dados não commitados) impossível por construção. READ COMMITTED (padrão) tira um snapshot novo a cada comando dentro da transação. REPEATABLE READ tira um único snapshot para a transação inteira, prevenindo non-repeatable reads e phantom reads — mas, diferente do padrão SQL, ainda não é serializável de verdade sozinho. SERIALIZABLE usa uma técnica chamada SSI (Serializable Snapshot Isolation) que detecta, em tempo real, quando o resultado de transações concorrentes divergiria de alguma ordem serial possível, abortando uma delas com erro de serialização — é o único nível que garante ausência total de anomalias, ao custo de exigir retry na aplicação.SERIALIZABLE exige que a aplicação trate o erro 40001 (serialization_failure) com retry automático — sem isso, transações vão falhar silenciosamente sob carga concorrente.MVCC elimina locks de leitura, mas não elimina locks — escritas concorrentes na mesma linha, e certas operações de DDL, ainda precisam coordenar acesso exclusivo a algo.
FOR UPDATE, FOR SHARE, aplicados quando duas transações tentam modificar a mesma linha) e locks de tabela (table-level: de ACCESS SHARE, o mais fraco, tirado por qualquer SELECT, até ACCESS EXCLUSIVE, tirado por um DROP TABLE ou certos ALTER TABLE). Um deadlock acontece quando duas transações esperam locks uma da outra em ordem cruzada; o Postgres detecta o ciclo automaticamente (a cada deadlock_timeout, 1s por padrão) e aborta uma das transações. A view pg_locks, cruzada com pg_stat_activity, é a ferramenta padrão para ver quem está esperando por quem em tempo real.-- Quem está bloqueando quem, agora SELECT blocked.pid AS blocked_pid, blocking.pid AS blocking_pid, blocked.query AS blocked_query FROM pg_stat_activity blocked JOIN pg_locks bl ON bl.pid = blocked.pid AND NOT bl.granted JOIN pg_locks kl ON kl.locktype = bl.locktype AND kl.granted JOIN pg_stat_activity blocking ON blocking.pid = kl.pid;
Todo o MVCC depende de comparar IDs de transação (XIDs) para decidir "isso é passado ou futuro?" — e esse contador é de apenas 32 bits, o que cria um dos poucos cenários catastróficos reais em operação Postgres.
freeze) as tuplas antigas — marcando-as como permanentemente visíveis, fora da contagem circular —, o contador dá a volta e tuplas antigas passam a parecer "do futuro", ou seja, invisíveis. Antes de chegar lá, o Postgres entra em modo de proteção: primeiro emite warnings agressivos, depois recusa novas transações até que um VACUUM manual libere XIDs. O autovacuum já prioriza tabelas próximas do limite de wraparound automaticamente — mas isso pode ser insuficiente se autovacuum estiver desabilitado ou mal ajustado numa tabela de altíssimo volume.age(datfrozenxid) em pg_database — valores acima de ~1,5 bilhão merecem atenção imediata.WAL, Durabilidade e Recuperação
O log que garante que nada commitado se perde
A regra de ouro do WAL: uma mudança só é considerada durável depois que o registro dela chegou em disco no log — nunca antes. Isso é o que sobrevive a uma queda de energia no meio de uma escrita.
pg_wal/ e só faz fsync desse log a cada COMMIT. As páginas de dados em si são gravadas depois, de forma preguiçosa, pelo background writer e pelo checkpointer. Se o servidor cair entre o commit e a gravação da página real, a recuperação (Passo 16) simplesmente reaplica o WAL a partir do último checkpoint, reconstruindo o estado correto. Esse é o mesmo princípio de write-ahead logging usado por praticamente todo SGBD transacional sério — o Postgres só o implementa de um jeito particular.Se o WAL crescesse para sempre, a recuperação após uma queda demoraria cada vez mais. O checkpoint é o ponto de corte que limita esse custo.
checkpoint_timeout (tempo máximo entre checkpoints, padrão 5min) e max_wal_size (volume de WAL que dispara um checkpoint antecipado). Checkpoints muito frequentes geram picos de I/O; checkpoints muito espaçados alongam o tempo de recuperação após uma falha — checkpoint_completion_target suaviza isso espalhando a escrita ao longo do intervalo em vez de rajadas.Por padrão o Postgres recicla WAL antigo assim que possível. Para backup contínuo e recuperação ponto-no-tempo (PITR), é preciso dizer explicitamente ao banco para guardar mais informação e arquivar os segmentos antes de reciclá-los.
wal_level controla quanta informação cada registro de WAL carrega: minimal (só o necessário para crash recovery), replica (padrão — inclui o suficiente para replicação física e PITR) e logical (adiciona metadados para replicação lógica — Fase 6). Continuous archiving copia cada segmento de WAL fechado para um armazenamento externo (S3, GCS, ou um servidor de arquivo) via archive_command, antes que o Postgres o recicle. Combinado com um backup base periódico, isso permite Point-in-Time Recovery: restaurar o banco para o estado exato de, por exemplo, "2 minutos antes de alguém rodar um DELETE sem WHERE" — reaplicando o WAL arquivado até o instante desejado.Quando o Postgres é reiniciado depois de uma queda (em vez de um shutdown limpo), ele não assume que os dados em disco estão corretos — ele os reconcilia automaticamente contra o WAL antes de aceitar conexões.
max_wal_size muito alto (checkpoints raros) alonga esse tempo de indisponibilidade após uma falha.VACUUM e Manutenção
O preço operacional de nunca sobrescrever uma tupla
Como visto na Fase 3, um UPDATE ou DELETE não remove nada fisicamente — só marca a versão antiga como morta. Alguém precisa, mais tarde, efetivamente liberar esse espaço.
Deixar VACUUM manual a cargo do operador não escala. O autovacuum é um daemon que monitora tabelas e dispara VACUUM (e ANALYZE) automaticamente — mas seus padrões de fábrica são conservadores demais para muitas cargas modernas.
autovacuum_vacuum_threshold + autovacuum_vacuum_scale_factor × n_tuplas (padrão: 50 + 20% do tamanho da tabela) — o que, numa tabela de 50 milhões de linhas, significa esperar 10 milhões de tuplas mortas antes de agir. Em tabelas grandes e de alto volume de escrita, isso é tarde demais; reduzir autovacuum_vacuum_scale_factor por tabela (via ALTER TABLE ... SET) é um dos ajustes de tuning mais comuns e de maior impacto em produção. autovacuum_vacuum_cost_limit e _cost_delay controlam quanto I/O o autovacuum pode consumir antes de pausar, equilibrando velocidade de limpeza contra impacto nas queries concorrentes.-- Autovacuum mais agressivo numa tabela de altíssima escrita ALTER TABLE eventos SET ( autovacuum_vacuum_scale_factor = 0.02, autovacuum_vacuum_cost_delay = 2 );
Bloat é o espaço físico ocupado por tuplas mortas e páginas parcialmente vazias que o VACUUM comum ainda não conseguiu devolver ao sistema operacional.
FULL) libera espaço de volta para a Free Space Map da própria tabela, para ser reaproveitado por INSERTs futuros — mas raramente devolve o espaço ao sistema operacional, porque isso exigiria mover dados para compactar páginas no fim do arquivo. O resultado, sob padrões de UPDATE intenso mal servidos pelo autovacuum, é uma tabela fisicamente maior que seus dados úteis, com índices B-tree também inflados por ponteiros para tuplas mortas. Sintomas típicos: tamanho em disco crescendo sem crescimento proporcional de linhas, e planos de execução ficando mais lentos porque scans percorrem mais páginas vazias. A correção estrutural (quando o bloat já está feito) é VACUUM FULL ou a extensão pg_repack — ambos reescrevem a tabela do zero, mas o primeiro exige lock exclusivo.VACUUM FULL trava a tabela inteira (leituras e escritas) enquanto reescreve — em produção, prefira pg_repack, que faz o mesmo trabalho online usando uma tabela sombra.Normalmente, um UPDATE precisa atualizar a nova versão da tupla em todo índice da tabela, mesmo que só uma coluna sem índice tenha mudado. Heap-Only Tuples é a otimização que evita isso.
fillfactor de folga (ex: 90 em vez de 100) numa tabela de UPDATE intenso aumenta a chance de cada UPDATE caber na mesma página e virar HOT.Replicação e Alta Disponibilidade
Cópias do WAL em outros servidores, e o que fazer quando o primário cai
Se o crash recovery (Fase 4) já reaplica o WAL localmente, a replicação física é a mesma ideia levada adiante: um segundo servidor reaplica continuamente o WAL de um primário remoto.
Por padrão, um COMMIT no primário é considerado concluído assim que o WAL é durável localmente — a réplica pode ainda não ter recebido nada. A replicação síncrona muda esse contrato.
synchronous_standby_names), o COMMIT só retorna depois que pelo menos uma réplica designada confirma ter recebido (ou aplicado, dependendo de synchronous_commit) o WAL — eliminando a janela de perda, ao custo de que a latência de cada escrita passa a incluir uma viagem de rede até a réplica. Um meio-termo comum é synchronous_commit = remote_write: a réplica confirma que recebeu o WAL (mas não necessariamente aplicou), reduzindo a latência frente a on completo.A replicação física copia bytes idênticos — mesma versão, mesmo esquema. A replicação lógica decodifica o WAL em mudanças de linha (INSERT/UPDATE/DELETE) e as reaplica via SQL, abrindo um leque de casos de uso diferente.
O PostgreSQL, sozinho, não tem failover automático — ele sabe replicar, mas não sabe decidir "o primário caiu, promova essa réplica agora". Essa decisão exige uma camada externa de orquestração.
Segurança e Controle de Acesso
Quem pode conectar, autenticar e ver o quê
No Postgres, um "usuário" é só uma role com a permissão LOGIN ativada —
roles e grupos são o mesmo conceito, o que torna o modelo de permissões mais uniforme (e mais fácil
de compor) do que em sistemas com dois conceitos separados.
GRANT role_a TO role_b), criando herança de privilégios: se role_b herda de role_a, ela automaticamente tem tudo que role_a tem, sem precisar de GRANTs redundantes. O padrão recomendado é separar roles de "grupo" (sem LOGIN, representando um papel: leitura_bi, app_writer) de roles de "pessoa/aplicação" (com LOGIN), concedendo o grupo à pessoa — assim, revisar quem tem acesso a quê vira uma questão de olhar as roles de grupo, não caçar GRANTs individuais espalhados.CREATE ROLE app_readonly; GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_readonly; CREATE ROLE svc_relatorios LOGIN PASSWORD '...'; GRANT app_readonly TO svc_relatorios;
Antes de qualquer role ou privilégio de tabela entrar em jogo, o Postgres decide se a conexão sequer é permitida — e isso é governado por um único arquivo, lido de cima para baixo.
pg_hba.conf (host-based authentication) declara: tipo de conexão (local, host, hostssl), banco, role, origem (CIDR) e método de autenticação. O Postgres avalia as linhas em ordem e usa a primeira que casar — regras mais específicas devem vir antes das genéricas. Métodos comuns: scram-sha-256 (senha, hash moderno — padrão recomendado hoje), cert (certificado de cliente TLS), peer/ident (identidade do SO, só para conexões locais) e trust (sem autenticação — nunca em produção, exceto talvez para local em desenvolvimento).trust ou 0.0.0.0/0 com senha fraca em pg_hba.conf são os dois erros de configuração mais comuns que resultam em bancos Postgres expostos publicamente na internet.
GRANT controla acesso a tabelas inteiras. Quando o requisito é "cada cliente só vê suas
próprias linhas", RLS aplica esse filtro automaticamente, sem depender da aplicação lembrar de um
WHERE tenant_id = ... em toda query.
CREATE POLICY) que o Postgres injeta automaticamente como filtro implícito em toda query contra a tabela — para SELECT, INSERT, UPDATE e DELETE separadamente, se necessário. É o mecanismo de isolamento de tenant mais robusto em arquiteturas multi-tenant com schema compartilhado: mesmo uma query mal escrita pela aplicação, sem filtro de tenant, não consegue vazar dados de outro cliente, porque o filtro vive no banco, não na camada de aplicação. RLS só se aplica a roles sem o atributo BYPASSRLS (superusers o ignoram por padrão) — um detalhe frequentemente esquecido ao testar políticas.ALTER TABLE pedidos ENABLE ROW LEVEL SECURITY; CREATE POLICY isolamento_tenant ON pedidos USING (tenant_id = current_setting('app.tenant_id')::uuid);
O Postgres nativamente não criptografa dados em repouso — isso normalmente vem de uma camada abaixo (disco, provedor cloud) ou de uma extensão, dependendo do requisito de compliance.
ssl = on em postgresql.conf, combinado com hostssl em pg_hba.conf, força TLS em toda conexão de rede. Em repouso: a abordagem mais comum é criptografia no nível do sistema de arquivos ou do volume (LUKS, EBS encryption, TDE do provedor cloud) — transparente para o Postgres. Para criptografia seletiva por coluna, a extensão pgcrypto oferece funções como pgp_sym_encrypt, embora isso impeça indexação eficiente sobre o valor cifrado. Auditoria: log_statement e log_connections nativos cobrem o básico; a extensão pgAudit é o padrão de mercado para trilhas de auditoria detalhadas exigidas por frameworks de compliance (SOC 2, PCI-DSS, LGPD/GDPR).Extensibilidade e Tipos de Dados
O que diferencia o Postgres de um SGBD relacional comum
CREATE EXTENSION é provavelmente o comando mais estratégico do Postgres — é a porta de
entrada para tudo que o transforma de "SGBD relacional" em "plataforma de dados" (geoespacial, vetorial,
full-text, time-series).
ALTER EXTENSION ... UPDATE). O catálogo pg_extension rastreia o que está instalado; pg_available_extensions mostra o que está disponível no servidor mas ainda não ativado. Extensões escritas em C rodam no mesmo processo do backend (máxima performance, mas exigem confiança — só superusers instalam extensões não marcadas trusted); extensões em SQL/PL puro são mais seguras e portáveis.CREATE EXTENSION IF NOT EXISTS pg_stat_statements; CREATE EXTENSION IF NOT EXISTS pgcrypto; SELECT * FROM pg_available_extensions ORDER BY name;
Antes de recorrer a uma extensão, vale lembrar que o Postgres já vem, nativamente, com tipos que cobrem boa parte do que outros SGBDs relacionais exigem uma tabela extra ou uma ferramenta externa para representar.
@>) — na prática, um documento semi-estruturado dentro de uma coluna relacional. Arrays são um tipo de primeira classe (integer[], text[]), com operadores próprios, evitando uma tabela de junção para listas pequenas e de tamanho conhecido. Range types (int4range, tsrange, daterange) representam um intervalo com operadores de sobreposição nativos (&&) — ideais para agendas, reservas e qualquer coisa com "período de validade". Composite types permitem definir um tipo com múltiplos campos, reutilizável como coluna ou retorno de função.Desde a versão 10, o Postgres suporta particionamento como recurso de primeira classe — a aplicação continua enxergando uma única tabela, mas o Postgres decide sozinho em qual partição cada linha vive.
PARTITION BY RANGE/LIST/HASH) não tem dados próprios — ela é só uma definição lógica; cada partição é uma tabela física real. O planner usa partition pruning: se a query filtra pela chave de particionamento, ele consulta só as partições relevantes, ignorando o resto. Isso ajuda tanto performance de leitura quanto manutenção — dropar uma partição inteira (ex: dados de um mês antigo) é uma operação de metadado quase instantânea, contra um DELETE que percorreria milhões de linhas e geraria bloat. A extensão pg_partman automatiza a criação e retenção de partições por tempo, um padrão comum para tabelas de eventos e séries temporais.CREATE TABLE eventos ( id bigserial, criado_em timestamptz NOT NULL, payload jsonb ) PARTITION BY RANGE (criado_em); CREATE TABLE eventos_2026_09 PARTITION OF eventos FOR VALUES FROM ('2026-09-01') TO ('2026-10-01');
Além das extensões de propósito específico (PostGIS para geoespacial, pgvector para busca vetorial), existe um pequeno conjunto que quase toda operação séria acaba instalando.
pg_stat_statements é, na prática, obrigatória: agrega estatísticas de execução por query normalizada (tempo total, chamadas, I/O), a base de qualquer diagnóstico de performance real — o estudo de otimização de queries do hub depende dela diretamente. pg_partman automatiza o ciclo de vida de partições (Passo 31). pg_repack resolve bloat sem lock exclusivo (Passo 19). pgAudit cobre trilhas de auditoria (Fase 7). PostGIS transforma o Postgres num SGBD geoespacial completo, com tipos de geometria e centenas de funções espaciais — motivo pelo qual muitas equipes de SIG escolhem Postgres em vez de um banco especializado proprietário.Configuração, Otimização e Migração
A ponte entre este guia e os dois estudos aplicados do hub
Os valores padrão de postgresql.conf são deliberadamente conservadores — pensados para
rodar em qualquer máquina, não para aproveitar o hardware real de um servidor de produção.
shared_buffers é o cache principal de páginas em memória compartilhada entre backends — regra prática: 25% da RAM disponível, não mais (o sistema operacional já faz cache de página por conta própria, e duplicar demais esse cache é desperdício). work_mem é o limite de memória por operação de ordenação/hash dentro de cada conexão — um valor alto demais multiplicado por muitas conexões concorrentes pode esgotar a RAM do servidor rapidamente. effective_cache_size não aloca nada; é só uma dica ao planner sobre quanta memória de cache (SO + Postgres) está disponível, influenciando se ele prefere um Index Scan ou um Seq Scan.Voltando ao Passo 2: cada conexão é um processo do SO. Um pooler resolve isso reutilizando um número pequeno de conexões reais para atender um número muito maior de clientes.
PREPARE ou variáveis de sessão), e statement (o mais agressivo, incompatível até com transações multi-comando). Pgpool-II é uma alternativa mais pesada que também faz balanceamento de carga entre réplicas e cache de query, mas com uma superfície operacional maior. A escolha do modo do PgBouncer é, na prática, uma negociação entre densidade de conexões e quais recursos SQL a aplicação pode abrir mão.Este guia cobriu por que o planner precisa de estatísticas, como índices e MVCC funcionam por dentro. Como usar isso para diagnosticar e corrigir uma query lenta específica é um estudo completo à parte.
EXPLAIN (ANALYZE, BUFFERS), o modelo de custos do planner, métodos de acesso (scans), algoritmos de join, os seis tipos de índice do Postgres (B-tree, Hash, GIN, GiST, SP-GiST, BRIN) e quando escolher cada um, anti-padrões comuns de query, e um laboratório reproduzível para praticar tudo isso com volume de dados realista.Toda estratégia de migração sem downtime é, no fundo, uma aplicação prática dos mecanismos de replicação da Fase 6 — este guia dá a teoria; o runbook do hub dá o passo a passo operacional.
pg_createsubscriber + pg_upgrade, pg_upgrade in-place, pglogical, CDC com Debezium/Kafka, e ferramentas gerenciadas de nuvem — além de um capítulo comum de preparação, cutover/validação/rollback, e troubleshooting rápido. A escolha entre estratégias depende essencialmente de três fatores: é upgrade de versão major ou só troca de infraestrutura, qual o tamanho do banco, e quanto tempo de indisponibilidade é aceitável — o runbook ajuda a decidir isso na primeira fase, antes de qualquer comando.