Qual o melhor tipo de ID para seu sistema? AUTO_INCREMENT, UUIDv4, UUIDv7 ou ULID?

🔑 AUTO_INCREMENT vs UUIDv4 vs UUIDv7 vs ULID

Ao construir bancos de dados e arquiteturas escaláveis, a escolha do tipo de ID é estratégica. Neste guia prático, você vai entender as diferenças entre:

  • AUTO_INCREMENT (inteiro sequencial)
  • UUIDv4 (aleatório)
  • UUIDv7 (tempo + aleatório)
  • ULID (ordenável + legível)

🧠 Comparativo Geral

Critério AUTO_INCREMENT UUIDv4 UUIDv7 ULID
🔢 Tipo Inteiro Aleatório Timestamp + Aleatório Timestamp + Aleatório
🧭 Ordenável ✅ Sim ❌ Não ✅ Sim ✅ Sim
🌍 Única no mundo ❌ Não ✅ Sim ✅ Sim ✅ Sim
🚀 Performance ✅ Alta ⚠️ Fraca em índices ✅ Alta ✅ Alta
🔐 Previsibilidade ❌ Sim ✅ Não previsível ✅ Não previsível ✅ Não previsível
🧾 Legibilidade ✅ Boa ❌ Ruim ⚠️ Média ✅ Alta
🧩 Padrão UUID ❌ Não ✅ Sim ✅ Sim ❌ Não (formato próprio)

📌 Quando usar cada um?

🔹 AUTO_INCREMENT

Simples e eficaz em bancos monolíticos. Ótimo para sistemas pequenos ou controlados.

⚠️ Desvantagem: fácil de prever, não funciona bem em ambientes distribuídos. ✅ Vantagem: ordenável e ocupa poucos bit.


🔹 UUIDv4

Totalmente aleatório. Funciona bem em sistemas distribuídos que não precisam de ordenação.

⚠️ Desvantagem: quebra a ordenação de índices → performance pior em bancos com muitas escritas. ✅ Vantagem: difícil previsibilidade.


🔹 UUIDv7

Usa timestamp + entropia randômica. É ordenável e globalmente único.

✅ Ideal para: APIs modernas, sistemas distribuídos, logs auditáveis e banco de dados com ordenação por createdAt.


🔹 ULID

Alternativa moderna e legível. Ordenável por tempo e ótimo para debug humano.

✅ Ideal para: sistemas onde legibilidade e ordenação importam (ex: logs, IDs visíveis em URL). ⚠️ Desvantagem: não utiliza padrão UUID.


✨ Exemplos reais de cada tipo de ID

Tipo Exemplo
AUTO_INCREMENT 1, 2, 3, 4, 5, ...
UUIDv4 f47ac10b-58cc-4372-a567-0e02b2c3d479
UUIDv7 68414cb4-cd2f-7d25-bf69-9d8f3ee2c41a
ULID 01HSHZK5WFP0RKNJ6XCVZ5R1AB

🏆 UUIDv7

🔍 Anatomia da UUIDv7 — Como ela funciona?

A UUIDv7 é uma evolução moderna do padrão UUID, que combina ordenabilidade por tempo com unicidade garantida via entropia aleatória.

Ela mantém os 128 bits tradicionais dos UUIDs, mas reorganiza esses bits de forma estratégica.

📐 Estrutura técnica da UUIDv7

Campo Bits Significado
Timestamp 48 Tempo atual em milissegundos desde 1970 (Unix Epoch)
Versão 4 Sempre 0111 (decimal 7) — indica que é um UUIDv7
Variante 4 Bits reservados para compatibilidade com o padrão UUID/RFC
Random 74 Entropia aleatória para garantir unicidade mesmo no mesmo milissegundo

🧪 Exemplo prático de UUIDv7

68414cb4-cd2f-7d25-bf69-9d8f3ee2c41a

🧪 Gerando UUIDv7 em Node.js

import { v7 as uuidv7 } from 'uuidv7';

const id = uuidv7();
console.log(id); // Ex: 01890d4c-cd2f-7d25-bf69-9d8f3ee2c41a

//EXPLICAÇÃO

//[68414cb4]   → Timestamp (milissegundos desde 1970)
//└── hex: 68414cb4
//   └── decimal/timestamp: 1749109940
//   └── ISO 8601: 2025-06-05T07:52:20.000Z

//[cd2f]       → Parte randômica

//[7d25]       → Versão + variante

//[bf69-9d8f3ee2c41a] → Mais 74 bits de entropia aleatória

💙 💻 ☕️ 😎 Se você chegou até aqui, espero ter colaborado em algo útil para você tomar as próxima decisões. Abraços!

Acho que um dos maiores problemas dos UUID são os tamanhos de 128 bits, que simplificando muito faz o processador "dar dois ciclos" para interpretar o número por completo. Além de usar como chave um número imenso difícil de memorizar, e que deve espalhar por toda a base de dados através das chaves estrangeiras.

Como muitos deves trabalham apoiados em ORMs, provavelmente problemas de legibilidade ficam maquiados, e só se tornam um problema mesmo quando é necessário fazer uma investigação nos dados.

Uma outra alternativa para IDs desse tipo é o SnowFlake ID, que gera IDs de 64 bits ordenáveis. Possui vantagens e desvantagens em relação aos UUID.


De qualquer forma, se a geração de IDs é centralizada, então quase sempre a melhor alternativa são os auto increment, rápidos, leves e atômicos. E se quiser criar um ID público para não expor o ID do registro, é só gerar uma coluna com UUIDv7 com índice com um nome como public_id, dessa forma os relacionamentos entre as tabelas usarão chaves inteiras simples e o mundo externo verá UUID.

Vamos repassar algumas coisas.

Autoincremento não é necessariamente um inteiro em alguns bancos de dados, inclusive pode ser usado uma fórmula própria, mas ok, entendi que quis limitar o escopo, até porque existe muito mais que 4 opções para chave primária única, por exemplo sequencial + Mac Address ou outras variações, até mesmo com identificadores mais curtos de qual máquina é, só para ficar em um que é relativamente popular (dê uma pesquisada nos ????flakes e outros com 64 bits em sequência e distribuído).

O tipo dos UIs não aleatório, é string ou numérico (considera-se bytes). A aleatoriedade não é o tipo.

A garantia de ser única do mundo não é absolutamente garantida, porém a chance de pegar duas iguais beira o impossível, podemos considerar garantida na prática.

Pode ver um pouco mais sobre o UUID, embora já esteja defasado o que tem lá continua válido.

Fácil de prever é uma característica boa e não ruim. As pessoas que acham que é ruim é porque acham que estão dando segurança, mas quem pensa assim vai acabar fazendo softwares inseguros.

É necessário um certo cuidado com UUIDv7, embora raro ele pode não dar o resultado que a pessoa espera, er por ser raro fica difícil perceber o problema (pequeno, reforço).

As pessoas tendem a dar mais importância para IDs distribuídos do que deveria. Tem casos para uso, mas em boa parte dos casos centralizar a obtenção do ID é o mais simples, eficiente e que dá menos problemas. Não faça overengineering.

S2


Farei algo que muitos pedem para aprender a programar corretamente, gratuitamente (não vendo nada, é retribuição na minha aposentadoria) (links aqui no perfil também).

Essa questão de como definir o ID sempre me deixa confuso com essas recomendações genéricas. Entendo que escolher uma opção que não seja fácil de prever pode ser boa, como um usuário do sistema, uma transação, uma nota fiscal, por exemplo. Agora, para diversas outras informações que não precisam trafegar via URL (aliás, dados sensíveis deveriam trafegar pela URL????), o incremental não funciona muito bem?
Não existe isso de ser fácil de prever. Quem acha que isso é que dá segurança não entende de segurança. Você dá acessa às pessoas ao que elas têm autorização para acessar, o resto não é para ela ter acesso, portanto se a pessoa prever algo e ela não pode acessar e o sistema foi feito por quem entende de segurança não há problema algum. Se ela acha que é melhor um ID longo para dificultar, está no jogo do tigrinho, até o invasor vai perder quase sempre, mas uma hora ela acerta. Dados sensíveis só devem ser acessados por quem tem direito a isso, ponto. Se só tiver um dado e o ID dele for 1, ele só deve ser acesso ao autorizado, qualquer pessoa que tente usar 1 para acessar não vai conseguir, simples assim. Mais uma vez, segurança não é contar com a sorte, quando a pessoa fala em ser menos previsível ela já está admitindo que ela aceita que sorte a foda se a alguém tiver virado pra lua ou achar uma técnica nova que aumente as chances de acerto. Quem faz isso deveria ser proibido de fazer softwares profissionais. É óbvio que existem casos que você precisa de um ID de acesso aberto para qualquer pessoa que o possua, mas isso costuma ser temporário e é exceção, é mais um ID de acesso do que um ID de banco de dados, ainda que seja aproveitado para isso (muitas implementações não usam o mesmo, preferem um ID para o DB e outro para expor publicamente). Reforço, não é a mesma coisa que estão falando aqui, é outro tipo de ID. Boa parte dos IDs públicos gerais são justamente para acesso de qualquer pessoa, então não existe isso de previsibilidade nesses casos. Se puder use um ID sequencial simples. Quase todos os sistemas podem. Até alguns sistemas que fazem cadastros de forma distribuída podem usar assim e na maioria dos casos, a centralização garante bem o funcionamento e o custo é de ter que esperar o efetivo cadastro para ter o ID no cliente que está fazendo o cadastro, o que geralmente fica na casa de poucos milissegundos, se muito, ou é possível fazer uma reserva (isso tem lá seu problema quando a pessoa desiste do cadastro, mas tem diversas soluções). Se realmente precisa de um cadastro distribuído (poucos sistemas realmente precisam disso) naquela tabela, então precisa usar um ID gerado dentro um certo padrão. Atualmente o ULID parece ser o mais adequado para isso, mas não tenho experiência prática. Se o espaço for um grande problema pode se usar uma variação do Snowflake, mas geralmente não é uma ideia tão maravilhosa porque só tem problemas com espaço quem tem muito cadastro e o risco de colisão aumenta. Não podemos descartar, mas quanto mais complicado é o ID, mais restrições de uso ele tem, menos deve ser usado por quem tem pouca experiência com engenharia de software.
Opa, valeu pela resposta. Agora compreendi melhor. Por mais que seja possível prever, se não tiver logado com a permissão adequada, não acessa. Simples e efetivo.

Haverá o dia que desenvolvedores deixarão de passar id de entidade de banco via url. Este é o mais simples das cagadas que devs fazem e que permite usuário explorar cadastros que a principio não deveriam ter acesso ou conhecimento.

O correto (olha minha autoridade aqui), é fazer como alguns caras feras (eu não sou um) fazem, que é usar um UUID (não importa a versão). Isto evita que o usuário fique incrementando o id para ganhar acesso a outros cadastros pois remove a previsibilidade.

Gostaria de escrever mais, mas me deu preguiça e o intestino me avisou que devo ir na casinha.

nanoid sendo o goat dos ids

Demais, eu curto aqueles que tem um prefixo antes para identificar visualmente sobre qual recurso aquele id é relacionado, tipo: `usr_8475871` `ord_1151515` `pro_5059696` Só não sei se tem alguma desvantagem relacionada a essa abordagem