Modelagem define o que o sistema vai poder fazer

Em sistema corporativo, modelagem não é exercício de pureza teórica. O desenho das tabelas determina quais consultas o negócio consegue fazer e a que custo. Normalizar reduz anomalias de escrita e protege a consistência; desnormalizar acelera leitura e cria risco de dado divergente. A decisão prática é onde garantir integridade: constraints no banco (chave estrangeira, unique, check) ou regras na aplicação.

Antes de escolher entre PostgreSQL e MySQL, responda a estas perguntas:

  • Qual é o padrão de leitura dominante: busca por identificador, por período, por status?
  • Quais filtros aparecem sempre juntos? Eles indicam os índices compostos necessários.
  • A chave primária é estável e curta? Isso importa muito no InnoDB.
  • Algum campo guarda estrutura variável, como atributos de produto? JSON pode ser melhor que uma tabela de chave e valor.
  • Com que frequência a tabela recebe escrita? Cada índice adicional pesa em INSERT e UPDATE.

Um exemplo recorrente em distribuidoras: a tabela de itens de pedido cresce todo mês e o relatório de faturamento por filial e período passa a travar no fechamento. Ao investigar, a causa costuma ser modelagem, não servidor: falta um índice composto na ordem usada no filtro (filial, data de emissão) e o status do pedido está gravado como texto livre, com variações de grafia que impedem qualquer agregação confiável. Padronizar o domínio e criar o índice correto resolve o gargalo sem trocar de banco.

Onde PostgreSQL e MySQL divergem de verdade

As duas engines resolvem os mesmos problemas com mecânicas diferentes, e a diferença aparece justamente quando o volume cresce.

  • Controle de concorrência: o PostgreSQL mantém versões de linha no próprio heap e depende do autovacuum para recuperar espaço. Atualizações frequentes na mesma linha geram bloat e exigem ajuste de autovacuum por tabela. O InnoDB, do MySQL, concentra o histórico no undo log e depende do purge.
  • Chave primária: no InnoDB a tabela é organizada pela chave primária e todo índice secundário carrega essa chave. Chave longa ou aleatória infla índices e espalha inserções. No PostgreSQL a organização é por heap, com tabelas auxiliares para valores grandes.
  • Conexões: o PostgreSQL usa um processo por conexão, o que torna um pooler praticamente obrigatório em aplicações com muitos workers. O MySQL usa threads e tolera melhor conexões ociosas.
  • Índices e tipos: o PostgreSQL oferece GIN para JSONB e arrays, busca por similaridade com pg_trgm e um ecossistema amplo de extensões. O MySQL tem índices funcionais e bom suporte a JSON, com menos extensões disponíveis.
  • Replicação: streaming e replicação lógica no PostgreSQL; binlog com GTID no MySQL. Em qualquer um dos casos, réplica não substitui backup testado.

Não existe engine melhor no abstrato. Existe engine mais adequada ao padrão de leitura, ao volume de escrita e à competência disponível no time.

Performance se investiga, não se adivinha

Antes de trocar de banco, leia o plano de execução. No PostgreSQL, EXPLAIN (ANALYZE, BUFFERS) mostra tempo por nó e separa leitura de cache e de disco. No MySQL, EXPLAIN ANALYZE e o slow query log cumprem papel parecido. A maior parte dos problemas tem causa conhecida:

  1. Índice composto com colunas em ordem diferente da usada no WHERE.
  2. Função ou conversão de tipo aplicada à coluna filtrada, anulando o índice.
  3. Consultas N+1 geradas pelo ORM, multiplicando idas ao banco.
  4. Estatísticas desatualizadas levando o planejador a escolher junções ruins.
  5. Tabelas grandes sem particionamento por data, o que encarece arquivamento e manutenção.
  6. Transações longas segurando versões antigas e, no PostgreSQL, impedindo o vacuum de avançar.

Vale lembrar que índice não é gratuito: cada um adiciona custo em toda escrita. Otimizar leitura é sempre uma negociação com a escrita.

Na prática

Para quem mantém software em produção, uma rotina mínima:

  • Versione migrations e trate alteração de schema como código, com revisão e plano de rollback.
  • Leia o log de consultas lentas e transforme as piores em backlog técnico com dono.
  • Revise planos após mudanças de volume, de versão do banco ou de índices.
  • Acompanhe bloat e autovacuum no PostgreSQL; acompanhe histórico e purge no InnoDB.
  • Limite conexões na aplicação e use pooler; conexão ociosa consome memória.
  • Teste com volume próximo do real, porque plano medido em tabela pequena engana.
  • Indexe a partir de consultas reais, não de intuição, e remova índices sem uso.
  • Defina quem pode executar DDL em produção e em qual janela.

Escolher entre PostgreSQL e MySQL é decisão de longo prazo, mas o que sustenta o sistema é mais simples: modelagem alinhada ao padrão de acesso e disciplina de medir antes de mudar.