Geralmente, quando vamos criar ou planejar uma tabela no banco de dados, começamos pelo ID. Parece uma escolha simples, mas na verdade uma decisão equivocada sobre a chave primária pode trazer várias dores de cabeça ao longo do tempo.
O Início Sequencial
O tipo mais tradicional e amplamente conhecido é o AUTO_INCREMENT (ou BIGINT / BIGSERIAL). Ele foi ensinado por décadas em faculdades e cursos por ser extremamente intuitivo e funcionar de forma nativa.
Suas vantagens técnicas são indiscutíveis para bases relacionais tradicionais. Ele ocupa apenas 8 bytes, garantindo alta performance em disco e árvores B-Tree do MySQL e PostgreSQL. As consultas e junções (JOIN) são também muito rápidas, pois o processador executa comparações entre números inteiros nativamente na memória com eficiência máxima de cache.
Agora imagina você acessar o seu pedido em um e-commerce e ver a seguinte URL:
Ao expor essa chave sequencial, qualquer pessoa descobre na hora a quantidade exata de transações da empresa, permitindo inclusive que bots raspem todo o banco iterando a sequência.
Outro problema crítico é o acoplamento distribuído, onde microsserviços ou sistemas offline não conseguem gerar IDs sem consultar a base central primeiro, tornando a gravação no banco um gargalo obrigatório.
A Era Distribuída
Conforme as aplicações evoluíram de monólitos para arquiteturas distribuídas e microsserviços, o AUTO_INCREMENT virou um gargalo grande. Para resolver o acoplamento centralizado e o vazamento de métricas nas URLs, a indústria migrou em massa para o UUID v4.
A grande vantagem foi permitir a geração descentralizada, onde qualquer microsserviço cria chaves universais sem consultar a base central. Por ser praticamente 100% aleatório e baseado em entropia criptográfica, ele garante sigilo absoluto, tornando impossível adivinhar a próxima chave ou extrair qualquer metadado do ID.
O problema é que o UUID v4 é muito ruim em questões de performance para bancos de dados relacionais.
Por ser totalmente aleatório, inserir um UUID v4 destrói o índice B-Tree. O banco tenta gravar registros em páginas de disco randômicas, provocando constantes Page Splits (divisões físicas de páginas), o que gera alta fragmentação e I/O de disco excessivo.
Veja na simulação abaixo como a aleatoriedade do UUID v4 fragmenta a estrutura do banco a cada inserção.
Simulação interativa de alocação de páginas de disco e page splits em índices B-Tree.
SQL CONSOLE
READONLYUUID v7
Após algumas pesquisas atrás da chave perfeita, o UUID v7 foi o que mais me chamou atenção. Ele entrega o melhor dos dois mundos ao combinar a unicidade universal do UUID v4 com a ordenação por tempo (ordered by time) utilizando timestamp.
Ele codifica a data e hora atual em seus primeiros 48 bits, seguidos por bits de versão e variante, e 74 bits de entropia aleatória. Na prática, IDs gerados cronologicamente mantêm-se sempre em ordem sequencial lexicográfica.
Essa ordenação temporal resolve o problema de performance no banco de dados. Como os primeiros bits progridem linearmente no tempo, cada nova chave inserida é naturalmente maior que a anterior. O banco grava novos registros no final do índice B-Tree de forma ordenada e contínua, eliminando as divisões de páginas caóticas.
Para visualizar esse ganho na prática, você pode comparar a escrita fluida e sequencial do UUID v7 contra a fragmentação caótica do UUID v4 diretamente no simulador de índice B-Tree abaixo.
Simulação interativa de alocação de páginas de disco e page splits em índices B-Tree.
SQL CONSOLE
READONLYVeja abaixo a declaração de tabelas utilizando UUID v7 no PostgreSQL 18+ (suporte nativo) e no MySQL (InnoDB com tipo BINARY(16)):
SQL SCRIPT
readonlyNo entanto, nenhuma escolha de arquitetura vem sem contrapartidas. O UUID v7 ocupa 16 bytes, o dobro de um BIGINT (8 bytes). Em tabelas com bilhões de linhas no MySQL (InnoDB), esse tamanho propaga-se por todas as chaves estrangeiras e índices secundários, exigindo mais memória RAM para manter o cache aquecido.
Existe também a questão da privacidade, já que os primeiros 48 bits do UUID v7 registram o timestamp em milissegundos e qualquer chave exposta em uma URL pública carrega o momento de sua criação. Passe o cursor sobre o trecho destacado na URL abaixo para ver a data decodificada.
Na prática, qualquer usuário ou bot consegue decodificar esse trecho inicial e descobrir o instante exato com precisão de milissegundos em que o registro foi criado no sistema.
Outras Abordagens
Além dos UUIDs tradicionais, existem duas alternativas derivadas criadas para resolver cenários bem específicos de alta performance e interoperabilidade, o ULID e o Snowflake ID.
O ULID (Universally Unique Lexicographically Sortable Identifier) foi concebido para ser uma alternativa visualmente mais amigável ao UUID. Composto por 128 bits, ele é codificado em 26 caracteres em Base32 (Crockford) (ex: 01ARZ3NDEKTSV4RRFFQ69G5FAV). Ele remove caracteres ambíguos (como I, L, O, 0), tornando-o perfeito para rotas de APIs REST. No entanto, se a base de dados não tiver suporte nativo ao tipo ULID, ele acaba sendo salvo como texto puro (VARCHAR), perdendo eficiência espacial.
Por outro lado, os Snowflake IDs (arquitetados originalmente pelo Twitter) combinam a compacidade de um número inteiro de apenas 8 bytes (64 bits) com a capacidade distribuída. Eles codificam o timestamp, a identificação da máquina (Worker ID) e um contador local, suportando taxas massivas de ingestão por segundo sem colisão. A grande contrapartida é a complexidade operacional, exigem infraestrutura externa de orquestração (como o Apache ZooKeeper) para gerenciar os Worker IDs e evitar colisões entre nós.
Na Base 10 não é possível separar os caracteres por texto. A verdadeira fragmentação do Snowflake ocorre a nível binário:
Qual Chave Escolher?
Para POCs ou projetos monolíticos simples, o AUTO_INCREMENT continua vantajoso pela sua extrema simplicidade e eficiência de 8 bytes.
Mas quando falamos de profissionalismo, microsserviços e escalabilidade, o UUID v7 consolida-se como o padrão ouro inegociável. Ele resolve o dilema entre geração descentralizada e performance em B-Trees. Deixe o ULID para APIs que exigem IDs curtos em texto, o Snowflake para ingestão massiva de eventos, e descarte o UUID v4 completamente em novas aplicações devido à degradação severa de índices e fragmentação física.
Neste artigo, optei por focar a análise exclusivamente em chaves primárias artificiais e independentes, deixando de lado chaves compostas ou naturais por possuírem dinâmicas próprias de modelagem.
