Dados e bancos de dados

Oracle vs PostgreSQL: arquitetura, dialeto SQL e migração

Intermediário10 min de leituraAtualizado

Esta página foi traduzida do inglês com a ajuda de IA e conferida com o original. Ler a versão em inglês

Oracle e PostgreSQL resolvem o mesmo problema com mecanismos diferentes, e essas diferenças importam muito antes de você esbarrar em um caso extremo. Este guia compara a arquitetura de processos e de armazenamento, a abordagem de MVCC, os dialetos SQL e procedurais, o particionamento, a disponibilidade e o licenciamento de cada um, e depois mostra o que realmente quebra numa migração e como escolher entre os dois com honestidade.

O que você vai aprender

  • Descrever o modelo de instância, banco de dados e schema de cada motor e como as conexões são atendidas
  • Explicar a consistência de leitura baseada em undo versus o versionamento no heap com vacuum, e o trabalho operacional que cada um exige
  • Traduzir construções comuns de SQL e PL/SQL para seus equivalentes no PostgreSQL
  • Comparar as opções de particionamento, replicação e alta disponibilidade, incluindo o custo de licenciamento
  • Planejar uma migração e identificar as construções que precisam ser reescritas em vez de convertidas

A pergunta Oracle ou PostgreSQL costuma ser feita como se fosse uma questão de velocidade. Quase nunca é. Os dois são motores relacionais maduros, com otimizador baseado em custo e MVCC, que aguentam com folga cargas maiores do que a maioria das organizações produz. As diferenças que importam são de arquitetura e de operação: como cada um guarda as versões antigas das linhas, como é administrado, o que acontece quando você precisa de alta disponibilidade e qual o valor da fatura no fim do ano.

Este guia compara os dois motores como estão em 2026 (PostgreSQL 17 e 18, e Oracle Database 19c e 23ai) e termina com o trabalho de migração que as equipes subestimam sistematicamente.

Instâncias, bancos de dados e schemas

O Oracle traça uma linha clara entre a instância (as estruturas de memória e os processos em segundo plano) e o banco de dados (os arquivos em disco). A instância é dona da System Global Area, que contém o buffer cache, o shared pool e o redo log buffer, e de um conjunto de processos em segundo plano com nome próprio: DBWn grava os blocos sujos, LGWR descarrega o redo, CKPT avança os checkpoints, SMON e PMON cuidam da recuperação e ARCn arquiva os redo logs. Desde o 12c a arquitetura é multitenant: um banco contêiner (CDB) abriga bancos plugáveis (PDBs), e um PDB pode ser desplugado de um contêiner e plugado em outro, o caminho de upgrade e consolidação mais limpo que qualquer um dos dois motores oferece. Dentro de um banco, um schema é um usuário: criar um usuário cria o seu schema, e os objetos são referenciados como owner.object.

O PostgreSQL tem um cluster: um processo supervisor postgres, um pool de buffers compartilhado e processos auxiliares para o WAL writer, o checkpointer, o background writer, o launcher do autovacuum e as estatísticas. Cada conexão de cliente é atendida por um processo backend separado, criado via fork no momento da conexão. Isso torna a abertura de conexões relativamente cara, e é por isso que um pooler como o PgBouncer é padrão na frente de qualquer aplicação que abre e fecha muitas conexões. Um cluster contém vários bancos de dados, cada um com seus schemas, que são namespaces e não usuários; uma sessão se conecta a exatamente um banco e não consegue fazer joins entre bancos sem postgres_fdw ou dblink. Para quem vem do Oracle, o mapeamento mental que funciona é: PDB do Oracle ≈ banco de dados do PostgreSQL, schema do Oracle ≈ schema do PostgreSQL, e a fusão usuário/schema do Oracle simplesmente não existe.

Duas respostas para o MVCC

Os dois motores oferecem aos leitores um snapshot consistente sem bloquear quem escreve. Eles chegam lá de formas diferentes, e essa diferença explica a maior parte do contraste na operação do dia a dia.

O Oracle atualiza a linha no lugar e grava a imagem anterior à alteração num undo tablespace. Uma consulta que precisa de um snapshot mais antigo o reconstrói percorrendo o undo. A vantagem é que as tabelas não acumulam versões mortas e o espaço é reaproveitado automaticamente. As desvantagens são que o undo é um recurso compartilhado e finito, e que um relatório demorado pode falhar com o clássico ORA-01555 snapshot too old quando o undo de que ele precisa já foi sobrescrito. A retenção de undo é um parâmetro de tuning com custo real.

O PostgreSQL nunca sobrescreve uma linha viva. Um UPDATE grava uma nova versão da tupla no heap e marca a antiga como morta; um DELETE apenas marca. Cada leitor pega a versão visível para o seu snapshot. Por padrão, nada pode falhar com “snapshot too old”, mas as tuplas mortas precisam ser recuperadas, e esse é o trabalho do vacuum. O autovacuum cuida disso automaticamente, e um sistema saudável não precisa de intervenção manual, mas vale conhecer três modos de falha:

  • Bloat de tabelas. Se o autovacuum não dá conta de uma tabela com muitas atualizações, as tuplas mortas se acumulam, a tabela e seus índices crescem, e as varreduras leem mais páginas do que deveriam.
  • Transações longas bloqueiam a limpeza. Uma sessão “idle in transaction” prende o snapshot mais antigo, e aí nada mais recente pode passar por vacuum em lugar nenhum. Manter as transações curtas importa ainda mais aqui do que no Oracle; veja JDBC e transações.
  • Wraparound do ID de transação. A visibilidade das linhas usa um contador de 32 bits, e o vacuum precisa congelar periodicamente as linhas antigas. Ignore os avisos por tempo suficiente e o banco passa a recusar novas escritas para se proteger.

As versões recentes vêm reduzindo a dor: o PostgreSQL 17 reescreveu o armazenamento de tuplas mortas do vacuum para usar bem menos memória e deixar de exigir várias passadas pelos índices, e o PostgreSQL 18 adicionou um subsistema de I/O assíncrono que melhora leituras sequenciais grandes. Os HOT updates, em que uma atualização que não toca nenhuma coluna indexada reaproveita a mesma página, já evitam o retrabalho nos índices em boa parte das cargas.

Dialeto SQL

O SQL padrão viaja bem; os detalhes, não. A tabela abaixo mostra o que quebra primeiro quando uma consulta escrita para um motor roda no outro.

AspectoOraclePostgreSQL
Tipo numéricoNUMBER(p,s), um único tipo para todos os númerosnumeric, além dos nativos integer, bigint, double precision
StringsVARCHAR2(n); '' é armazenado como NULLvarchar(n), text; '' é uma string vazia, diferente de NULL
DatasDATE inclui horário; TIMESTAMP WITH TIME ZONE para offsetsdate não tem horário; use timestamptz
Limitar linhasFETCH FIRST n ROWS ONLY, o legado ROWNUMLIMIT n OFFSET m, também aceita FETCH FIRST
Tabela fictíciaSELECT sysdate FROM dualSELECT now(), sem precisar de FROM
Valor alternativo para NULLNVL, NVL2, DECODECOALESCE, CASE
Caixa dos identificadoresNomes sem aspas viram MAIÚSCULASNomes sem aspas viram minúsculas
HierarquiasCONNECT BY PRIORWITH RECURSIVE (também suportado pelo Oracle)
Hints do otimizadorHints /*+ ... */ de primeira classeSem hints nas instruções, por decisão de projeto; parâmetros do planner ou a extensão pg_hint_plan

A linha da caixa dos identificadores causa mais bugs de migração do que deveria. Uma ferramenta que gera CREATE TABLE "SESSION" no estilo Oracle deixa você preso a aspas duplas e maiúsculas para sempre no PostgreSQL. Converta os identificadores para minúsculas e deixe-os sem aspas.

Na parte procedural, PL/SQL e PL/pgSQL são parecidas (DECLARE/BEGIN/EXCEPTION/END, cursores, %ROWTYPE, RAISE), e rotinas simples são convertidas quase mecanicamente. O que consome tempo são as lacunas estruturais:

  • Packages não têm equivalente. Um package vira um schema de funções, e o estado no nível do package vira uma tabela, uma tabela temporária ou um GUC personalizado.
  • Transações autônomas não existem. Padrões de log de auditoria que dependiam delas são reconstruídos com um background worker, com dblink apontando para o próprio banco ou gravando o log fora da transação.
  • Os packages DBMS_ têm equivalentes diferentes caso a caso: DBMS_OUTPUT vira RAISE NOTICE, DBMS_SCHEDULER vira pg_cron ou um agendador externo, UTL_FILE vira COPY no servidor ou uma extensão.
  • Procedures versus funções. Funções do PostgreSQL sempre rodam dentro da transação de quem as chama; só CREATE PROCEDURE (a partir da versão 11) pode fazer COMMIT.

O PostgreSQL responde com algo que o Oracle não tem: qualquer função pode ser escrita em outra linguagem (plpython3u, plv8, Rust ou C) e instalada como extensão.

Particionamento, disponibilidade e extensões

Particionamento. O PostgreSQL tem particionamento declarativo desde a versão 10, com estratégias por range, list e hash, partition pruning tanto no planejamento quanto na execução, joins e agregações partição a partição, e attach/detach de partições como operação apenas de metadados. Sua única limitação relevante é que os índices são por partição: não existe índice global, então uma constraint de unicidade precisa incluir a chave de particionamento. O particionamento do Oracle é mais antigo, mais amplo (interval, reference, system, composite) e suporta índices globais; e, na Enterprise Edition, é uma opção licenciada à parte, o que surpreende equipes que achavam que estava incluída.

Alta disponibilidade. O Real Application Clusters do Oracle roda várias instâncias sobre armazenamento compartilhado, para escalar em ativo-ativo e fazer failover entre instâncias; o Data Guard mantém standbys físicos ou lógicos, com modos opcionais sem perda de dados. O PostgreSQL não tem clustering com disco compartilhado. Ele oferece replicação física por streaming, com commit síncrono ou assíncrono e réplicas de leitura em hot standby, além de replicação lógica para cópias seletivas, entre versões e entre plataformas. O failover automático vem do Patroni, do repmgr ou de um serviço gerenciado de nuvem, e não do núcleo do produto. Os dois modelos atingem as mesmas metas de disponibilidade; só um deles é uma caixa de seleção que você compra.

Extensões são o verdadeiro diferencial do PostgreSQL. pg_stat_statements para identificar consultas por fingerprint, PostGIS para dados geoespaciais, pgvector para embeddings, TimescaleDB para séries temporais, postgres_fdw para federação, pg_partman para manutenção de partições: tudo instalado com um único CREATE EXTENSION. No Oracle, os equivalentes são embutidos (Spatial, Text, In-Memory column store, Advanced Compression), o que é conveniente, e licenciados à parte, o que não é.

Diagnóstico merece uma menção porque os hábitos não se transferem. Os relatórios AWR e ASH e as views V$ do Oracle são imbatíveis para análise retrospectiva, mas exigem o Diagnostics Pack. O PostgreSQL expõe pg_stat_activity, pg_stat_user_tables, pg_stat_statements e EXPLAIN (ANALYZE, BUFFERS); para olhar para trás, você mesmo precisa amostrar esses dados numa stack de monitoramento. Ler os planos de qualquer um dos dois motores é o tema do guia de índices e planos de execução.

Licenciamento e custo

O Oracle Database Enterprise Edition é licenciado por núcleo de processador (com um fator de núcleo) ou por usuário nomeado, com uma assinatura de suporte de cerca de 22% da licença por ano, e a maioria das funcionalidades interessantes (particionamento, In-Memory, Active Data Guard, os packs de diagnóstico e tuning, RAC) é cobrada à parte. A Standard Edition 2 é mais barata, mas tem limite de sockets e threads e exclui a maioria das opções. A Express Edition é gratuita, com limites rígidos.

O PostgreSQL é distribuído sob a PostgreSQL Licence, uma licença permissiva no estilo BSD, sem custo por núcleo, sem auditoria e sem funcionalidades travadas. Ainda se gasta dinheiro (com serviços gerenciados, suporte comercial e o tempo de engenharia que as funcionalidades empacotadas do Oracle teriam coberto), mas o gasto acompanha o que você usa, e não quantos núcleos você tem.

Escolher e migrar

Escolha o Oracle quando precisar de clustering ativo-ativo sobre armazenamento compartilhado no estilo do RAC, quando cargas reguladas dependerem de Flashback Database ou de auditoria de granularidade fina, quando uma aplicação de fornecedor for certificada só para Oracle, ou quando um grande parque de PL/SQL fizer a migração custar mais do que as licenças. Escolha o PostgreSQL como padrão para desenvolvimento novo: ele segue os padrões, é barato de operar em qualquer escala, é extensível e portável para qualquer nuvem. Em 2026, a maioria dos sistemas novos não tem nenhum requisito que só o Oracle atenda.

Se for migrar, planeje quatro frentes de trabalho e reconheça desde já que as duas últimas são as que pesam:

  1. Conversão do schema. Em grande parte automatizada pelo ora2pg. Fique de olho em NUMBER sem precisão (não mapeie tudo para numeric; use bigint ou integer quando os dados permitirem), na conversão de DATE para timestamp e na caixa dos identificadores.
  2. Movimentação de dados. Exportação em massa e COPY para uma virada única; replicação lógica ou change data capture para uma virada com downtime próximo de zero.
  3. Reescrita de código. Packages, transações autônomas, hints, CONNECT BY, truques com ROWNUM e tudo o que usa DBMS_. Isso é redesenho de verdade, não conversão.
  4. Testes de comportamento. String vazia versus NULL é a clássica alteração silenciosa de dados; conversões implícitas que o Oracle tolerava viram erros; o cache de sequences e o RETURNING mudam a forma como as chaves geradas são devolvidas. Rode a mesma carga nos dois motores e compare os resultados antes de comparar os tempos.

A qualidade do schema sobrevive à mudança de qualquer forma: um design bem normalizado, como mostrado em design de banco de dados e normalização, e os hábitos de consulta do guia de boas práticas de SQL valem igualmente para os dois motores.

Principais pontos

  • O Oracle separa a instância do banco de dados, com processos em segundo plano nomeados e PDBs multitenant; o PostgreSQL cria um backend por conexão dentro de um cluster de bancos, por isso um pooler de conexões é essencial.
  • A consistência de leitura baseada em undo dá ao Oracle reaproveitamento automático de espaço, com o risco de ORA-01555; o versionamento no heap do PostgreSQL depende do autovacuum, e o que o quebra são as transações longas.
  • As diferenças de SQL que doem são string vazia versus NULL, a semântica de DATE, a caixa dos identificadores e a limitação de linhas; packages de PL/SQL e transações autônomas não têm equivalente no PostgreSQL.
  • O particionamento declarativo do PostgreSQL é competente, mas não tem índices globais; o do Oracle é mais rico e licenciado à parte.
  • O Oracle vende alta disponibilidade e diagnóstico como opções pagas; o PostgreSQL conta com replicação por streaming mais Patroni e com um ecossistema de extensões que não custa nada adotar.
  • Escolha PostgreSQL como padrão para sistemas novos, Oracle para capacidades específicas que você consiga nomear, e faça o orçamento de uma migração em torno da reescrita de código e dos testes de comportamento, não da conversão do schema.

Fontes e leituras complementares

Perguntas frequentes

O PostgreSQL é um substituto realista para o Oracle numa grande empresa?
Para a maioria das aplicações transacionais, sim, e esse tipo de migração já virou rotina. A dificuldade não está na capacidade do motor, e sim em todo o investimento feito ao redor dele: milhares de linhas de packages, jobs escritos para o DBMS_SCHEDULER, ferramentas construídas sobre o AWR ou uma dependência forte do Real Application Clusters. Avalie o parque de aplicações, não o motor, antes de decidir: o banco em si raramente é o impedimento.
Por que o PostgreSQL precisa de VACUUM e o Oracle não?
O PostgreSQL guarda todas as versões de uma linha na própria tabela, então uma linha atualizada ou excluída deixa para trás uma versão morta que só o vacuum consegue recuperar. O Oracle grava a imagem anterior em segmentos de undo e sobrescreve a linha no lugar, de modo que o espaço é reaproveitado automaticamente, mas consultas longas podem falhar quando o undo de que precisam já foi sobrescrito. É outro trade-off, não uma funcionalidade que falta.
Na prática, PL/SQL e PL/pgSQL são muito diferentes?
A estrutura de blocos, os cursores, o tratamento de exceções e as declarações de variáveis são parecidos o bastante para que procedures simples sejam convertidas quase linha a linha. As lacunas são estruturais: o PostgreSQL não tem packages, nem transações autônomas, nem uma biblioteca DBMS_ embutida, então esses pontos precisam ser redesenhados como schemas, background workers ou funções de extensões, e não traduzidos mecanicamente.
Qual motor é mais rápido?
Nenhum, pelo menos não de um jeito que dê para generalizar. Os dois usam otimizadores baseados em custo, MVCC e índices B-tree, e os dois escalam para cargas muito maiores do que a maioria das aplicações gera. Em sistemas reais, as diferenças de desempenho vêm do design do schema, da indexação, do formato das consultas e do hardware. Escolha pela adequação operacional, pelo ecossistema e pelo custo, e depois meça a sua própria carga com os seus próprios dados.