Pular para o conteúdo
Casos Técnicos

UUID v4 vs v7: quando migrar e o que esperar de ganho real

Por que UUIDs v4 aleatórios fragmentam o índice do seu banco e como o v7, ordenado por tempo, devolveu +260% de throughput no módulo de relatórios de um SaaS de varejo europeu.

Casos TécnicosJohnny Carreiro·29 de janeiro de 2026·4 min de leitura

Um cliente nos procurou convencido de que precisava reescrever o sistema inteiro em Go. O sintoma estava no módulo de relatórios de vendas de um SaaS de varejo europeu: os relatórios ficavam mais lentos conforme o intervalo crescia (mês → trimestre → semestre), e essa latência sangrava para as inserções e para outros serviços internos. Profiling e benchmarking apontaram o gargalo para outro lugar — o banco de dados, não a linguagem — e a solução não trocou uma linha de linguagem.

O problema do UUID v4

UUID v4 é aleatório por design. Cada novo identificador cai em uma posição imprevisível do espaço de chaves. Isso parece inofensivo até você lembrar como um índice B-tree do PostgreSQL funciona: ele mantém as chaves ordenadas em páginas. Quando você insere chaves em ordem (ou quase), as inserções caem nas últimas páginas e o índice cresce de forma compacta. Quando você insere chaves aleatórias, cada inserção pode cair em qualquer página — espalhando escritas por toda a árvore.

O resultado é fragmentação: o índice incha, as páginas ficam parcialmente cheias, o cache do banco fica menos eficiente porque páginas "quentes" estão espalhadas, e cada inserção pode forçar um page split. Em volume baixo isso não aparece. Em bancos distribuídos com bilhões de registros, vira o gargalo dominante.

Por que o v7 resolve

UUID v7 (padronizado na RFC 9562) coloca um timestamp de milissegundos nos bits mais significativos, seguido de bits aleatórios. Na prática, isso torna os UUIDs ordenáveis por tempo: identificadores gerados em sequência ficam próximos no espaço de chaves.

Para o índice B-tree, isso muda tudo. As inserções voltam a cair em páginas adjacentes, como uma chave sequencial faria, mas você mantém as vantagens do UUID — geração distribuída sem coordenação central, sem expor contagem de registros, sem colisão prática. É o melhor dos dois mundos: a unicidade global do UUID com o comportamento de localidade de uma chave sequencial.

O ganho real

Neste caso, a migração para v7 entregou +260% de throughput de inserção e cerca de -40% no tempo de queries de range — porque registros próximos no tempo agora estão próximos no índice e no disco. Tudo isso com zero downtime e rollback preparado, instrumentado com Jaeger para validar cada etapa em tracing distribuído. A reescrita completa para Go que o cliente cogitava teria custado meses e não resolveria nada: o gargalo nunca esteve na linguagem da aplicação.

Como migrar com segurança

Migrar chave primária em tabelas grandes — ainda mais distribuídas — exige cuidado. O caminho que usamos evitou um big-bang e uma migration de schema destrutiva:

  1. Tabela de lookup mapeando o id v4 ↔ o novo uuid v7 — a fonte de verdade da correspondência durante a transição. Uma ferramenta customizada em Rust gerou os v7 e populou o mapa para os bilhões de registros, em janelas controladas.
  2. Trocar o id pelo v7 progressivamente nas tabelas, consultando a lookup — sem uma coluna nova convivendo com a antiga.
  3. Propagar o v7 nas relações e referências antes de mexer no índice: tanto foreign keys tradicionais quanto referências informais (um valor "referenciado" entre bancos distintos, sem FK formal) passam a apontar para o v7.
  4. Reconstruir o índice aos poucos, à medida que o v7 já estava propagado — evitando o custo e o lock de recriar tudo de uma vez.
  5. Medir antes e depois — sem benchmark comparativo, você não sabe se ganhou.

Não foi o caminho mais óbvio, mas funcionou sem downtime. Como o v7 carrega um timestamp, não dá para derivá-lo do v4: um mapa de correspondência é mesmo a peça central — e cada caso pede o seu próprio plano.

Quando vale a pena

Migrar UUID v4 para v7 vale quando: você tem tabelas grandes (dezenas de milhões de linhas ou mais), usa UUID como chave primária, e observa inserção lenta ou índices inchados em relação ao volume de dados. Para projetos novos, é simples: comece em v7 e evite o problema desde o início.

Não vale o esforço em tabelas pequenas, onde a fragmentação é irrelevante. Como sempre em performance, a decisão é guiada pela métrica, não pela moda. Mas quando o sintoma bate, a migração para v7 é um dos melhores retornos sobre esforço que existem — corrige a causa raiz sem reescrever a aplicação.