UUID e ULID são mecanismos usados em sistemas distribuídos e bancos de dados para emitir “IDs únicos”. Eles parecem semelhantes, mas variam em pontos como padronização, eficiência de armazenamento, legibilidade humana e risco de exposição de metadados. A seguir, organizo os principais aspectos e critérios de escolha de três formatos centrais — UUID v4, UUID v7 e ULID — e, ao final, trago observações resumidas sobre UUID v1 / v6.

Confira as ferramentas que geram IDs e recuperam informações de tempo diretamente no navegador, sem enviar dados:


UUID v4: totalmente aleatório

UUID v4 reserva 122 dos 128 bits para números aleatórios, o que mantém a probabilidade de colisão extremamente baixa. Em contrapartida, ele não obedece à ordem temporal, logo índices baseados em B-Tree sofrem com divisões frequentes de página e perda de localidade. É indicado para sessões, tokens de uso único e outros casos onde basta ter aleatoriedade pura.


UUID v7: a evolução ordenável por tempo

Padronizado em RFC 9562 (2024), o UUID v7 usa os 48 bits iniciais para o timestamp em milissegundos da época Unix, preenchendo o restante com aleatoriedade. A ordem lexicográfica da string coincide com a ordem de geração, então mesmo como chave primária mantém boa localidade de índice. Também pode ser gravado diretamente em tipos uuid ou BINARY(16) existentes.

  • Comprimento da parte aleatória: descontando os bits de version/variant, o v7 preserva cerca de 74 bits de entropia aleatória. Implementações podem aplicar geração monotônica (para evitar colisões no mesmo milissegundo) e reajustar alguns bits, sem perder a qualidade estatística.
  • Risco de exposição: é possível inferir o momento de geração (precisão de milissegundo) a partir do ID. Em cenários de alto volume com PRNG fraco, fica relativamente mais fácil estimar IDs vizinhos. Em sistemas internos isso costuma ser aceitável, mas endpoints públicos que não podem sugerir sequência precisam de cuidado.

ULID: o ID mais amigável para pessoas

ULID, proposto em 2016, tornou-se um padrão de fato. Ele usa 26 caracteres do Crockford Base32, removendo símbolos que causam confusão (O/I/L/1), o que facilita uso em URLs e cópias. Assim como o v7, os 48 bits iniciais guardam o timestamp em milissegundos, garantindo que a ordem lexicográfica corresponda ao tempo.

  • Garantia de ordenação: comparar strings ULID em Base32 sempre preserva a ordem cronológica; a geração monotônica também é amplamente suportada.
  • Formato de armazenamento: TEXT(26) oferece boa leitura; para armazenar em binário no banco é preciso converter entre Base32 e 16 bytes. O tamanho continua sendo 128 bits, mas tipos nativos uuid raramente aceitam ULID.
  • Risco de exposição: igual ao do v7, com o timestamp visível. Em APIs públicas que não podem revelar a cadência de criação, é necessário avaliar o trade-off.

Tabela comparativa (visão prática)

Item UUID v4 UUID v7 ULID
Padronização RFC 4122 RFC 9562 (2024) Não padronizado (padrão de fato)
Estrutura em bits 122 bits aleatórios em 128 48bit=UNIX ms + resto aleatório (com suporte a monotonicidade) 48bit=UNIX ms + 80bit aleatórios
Entropia aleatória ≈122 bits ≈74 bits (estimativa após version/variant) 80 bits
Representação textual 36 caracteres (hex + hífens) 36 caracteres (hex + hífens) 26 caracteres (Crockford Base32)
Ordenação natural (texto) × (aleatório) ○ (ordem lexicográfica = tempo) ○ (ordem lexicográfica = tempo)
Ordenação natural (binário) × ○ (BINARY(16) com memcmp crescente) ○ (se os 6 bytes de tempo ficarem em big-endian, memcmp cresce com o tempo)
Armazenamento em banco uuid / BINARY(16) são ideais uuid / BINARY(16) são ideais TEXT(26) é legível; BINARY(16) exige conversão / normalmente incompatível com tipos UUID
Localidade de índice Baixa (inserções aleatórias dividem páginas) Alta (append no final / monitorar hot spot) Alta (append no final / monitorar hot spot)
Custo de igualdade Baixo (comparação de 16 bytes) Baixo (comparação de 16 bytes) TEXT é um pouco maior (depende de collation) / BINARY é baixo
Sobrecarga de codificação Hex↔16 bytes (leve) Hex↔16 bytes (leve) Base32↔16 bytes (um pouco maior)
Legibilidade / uso em URL Baixa Baixa Alta
Exposição de tempo Não Sim (milissegundos) Sim (milissegundos)
Casos principais IDs de sessão, tokens descartáveis Chaves primárias, IDs de eventos, logs ordenados URLs, IDs públicos, visualização de logs

Observação sobre hot spot em v7/ULID: ao inserir somente na extremidade de um único shard ou nó líder, o final do B-Tree pode superaquecer. Considere embaralhar levemente os bits superiores (“prefix shuffle”) ou combinar com chaves de sharding.


Notas de implementação em bancos (PostgreSQL / MySQL / SQLite)

  • PostgreSQL
    • v4/v7: o tipo uuid é a melhor opção. Armazenar v7 como string em uuid mantém a ordenação temporal.
    • ULID: utilize char(26) / text, ou converta para bytea(16).
  • MySQL/InnoDB
    • BINARY(16) é rápido e econômico para v4/v7. No v7, o layout big-endian permite usar memcmp para seguir o tempo. ULID pode ser CHAR(26) ou BINARY(16) com conversão.
  • SQLite
    • Não há tipo nativo de UUID. Use BLOB(16) ou TEXT. BLOB indexado oferece desempenho aceitável.

Reforços de segurança

  • Resistência a predição: v7/ULID compartilham os mesmos bits de tempo; se muitos IDs são gerados no mesmo milissegundo e o PRNG for fraco, prever IDs vizinhos fica mais fácil. Recorra a PRNG criptográfico do sistema operacional e minimize viés na geração monotônica.
  • Vazamento de metadados: é possível estimar o instante de emissão e o volume aproximado apenas lendo o ID. Em APIs públicas, prefira separar IDs internos e externos.

Resumo das recomendações

  • UUID v4: 100% aleatório, sem ordenação. A colisão ainda existe em teoria. Adequado para sessões ou tokens CSRF.
  • UUID v7: UUID ordenável e padronizado. Melhor candidato a chaves primárias internas. A geração monotônica evita colisões na prática.
  • ULID: mais “humano” e fácil de ler. Excelente para IDs expostos ou logs visíveis, embora costume perder para o v7 como chave interna. Também se beneficia da geração monotônica.

Apêndice: UUID v1 / v6 em poucas linhas

  • UUID v1 (tempo + MAC) Contém timestamp de 60 bits (resolução de 100 ns) e identificador de nó (frequentemente o endereço MAC). Garante ordem temporal forte, porém expõe dados da máquina e exige tratar cuidadosamente recuos de relógio. Hoje costuma ser evitado por motivos de privacidade.

  • UUID v6 (v1 reordenado) Reorganiza o timestamp do v1 em big-endian para favorecer a ordenação lexicográfica. Já foi visto como “um v1 fácil de ordenar”, mas o processo de padronização convergiu para o v7. Projetos novos tipicamente escolhem v7 em vez de v6.


Checklist de implementação

  • Use PRNG criptográfico fornecido pelo sistema operacional.
  • Na geração monotônica de v7/ULID, evite colisões dentro do mesmo milissegundo e reduza tendências na parte aleatória.
  • Prefira uuid / BINARY(16) no banco para não sofrer com collation de colunas textuais.
  • Defina a política de IDs públicos (se podem expor a ordem de emissão). Quando necessário, separe IDs internos e externos.

Conclusão: hoje UUID v7 é o candidato principal para chaves primárias internas. Quando legibilidade ou restrições técnicas pesarem mais, ULID é uma alternativa sólida. Somente requisitos que proíbam expor timestamps justificam recorrer ao v4 ou a outro esquema.