Modelagem decide a performance antes do primeiro índice

Todo problema de performance em banco relacional começa antes da consulta lenta: começa no formato das tabelas. Um índice novo se resolve em uma tarde. Trocar a chave primária de uma tabela grande em produção, não. Por isso modelagem é decisão de negócio: ela define quanto custa crescer, quanto custa mudar de regra e quanto custa manter o sistema rodando.

PostgreSQL e MySQL resolvem o mesmo problema por caminhos diferentes, e ignorar isso no desenho do schema cobra caro depois. No InnoDB, a chave primária é o índice clusterizado: os dados ficam organizados por ela e todo índice secundário carrega essa chave internamente. Uma chave primária grande ou aleatória infla todos os índices da tabela, não apenas um. No PostgreSQL a tabela é um heap e a chave primária é um índice separado, o que dá mais liberdade, mas exige saber que a ordem física dos dados não acompanha a chave sem manutenção explícita.

Tipos, chaves e constraints: onde o modelo vaza para o custo

Decisões de tipo parecem detalhe e não são:

  • Valores monetários em numeric, nunca em ponto flutuante. Arredondamento no banco vira divergência contábil no fechamento.
  • Datas com fuso em timestamptz no PostgreSQL e convenção explícita de UTC no MySQL. Fuso implícito é relatório errado esperando acontecer.
  • Chave estrangeira com o mesmo tipo e a mesma collation da chave referenciada. Tipo divergente impede uso de índice na junção e transforma consulta simples em varredura completa.
  • uuid como chave primária apenas quando houver necessidade real, como geração no cliente ou distribuição entre nós. No InnoDB, uuid aleatório fragmenta a inserção e aumenta o tamanho de todos os índices secundários.
  • not null, check e unique não são burocracia: impedem que dado inválido chegue ao relatório e evitam tratamento defensivo espalhado pelo código de aplicação.

Considere uma distribuidora que guardou o endereço de entrega como texto livre no pedido. A operação funcionou por um bom tempo. Quando a diretoria pediu relatório de frete por região, o dado não existia de forma consultável: o time passou a extrair cidade por expressão regular em toda a base, e a correção exigiu nova estrutura, migração em lotes e convivência entre os dois formatos por um período. O custo não veio da consulta, veio do modelo.

Normalizar, desnormalizar e o lugar do JSON

Terceira forma normal continua sendo o padrão sadio para dados transacionais: uma verdade em um lugar só, atualização barata e menos inconsistência. Desnormalizar faz sentido quando a leitura domina e a duplicação é controlada por um único ponto de escrita, como um total consolidado mantido por gatilho ou por rotina da aplicação.

JSON no PostgreSQL (jsonb) e no MySQL é útil para atributos esparsos e payloads de origem externa que não participam de junção nem de constraint. Vira dívida quando campos que precisam de validação, relatório e índice passam a morar dentro do documento. A regra prática: se o campo aparece em filtro recorrente, em regra de negócio ou em obrigação fiscal, ele merece coluna própria.

Na prática

Para quem mantém software em produção, modelagem aparece em decisões pequenas e frequentes:

  1. Antes de criar índice, verifique se o problema é consulta ou desenho. Índice sobre coluna mal tipada resolve pouco.
  2. Trate migração de schema como mudança de produto: script versionado, ensaio em base com volume realista, janela definida e plano de reversão.
  3. Padronize convenções de nomes, tipos de chave, fuso e collation, e registre isso em um documento curto. Consistência reduz erro e acelera revisão de código.
  4. Revise o modelo a cada nova entidade relevante, porque é ali que o custo futuro é decidido, não no meio do incidente.