Gerador de UUID

Gere UUID um a um ou cinquenta por vez: v4 aleatórios, ou v7 ordenados por tempo, cujo timestamp mantém o lote em ordem e serve de chave de banco de dados.

Versão

122 bits aleatórios. Não revela nada sobre quando foi criado.

Quantos

Gerando…

O que é um UUID e porque existe

Um UUID — Universally Unique Identifier, chamado GUID na documentação da Microsoft — é um valor de 128 bits escrito como 32 dígitos hexadecimais no familiar agrupamento 8-4-4-4-12. O seu propósito é permitir que sistemas separados criem identificadores de forma independente, sem qualquer coordenação entre si, e ainda assim confiarem em que os resultados não colidirão. É isso que o distingue de uma coluna auto-incremental: dois servidores, dois clientes móveis offline num avião e uma tarefa em segundo plano podem criar registros no mesmo instante sem pedir autorização a ninguém.

A unicidade aqui é probabilística e não garantida. A versão 4 deixa 122 bits ao acaso, um espaço suficientemente grande para que, mesmo gerando bilhões de valores, a probabilidade de uma repetição continue muito abaixo da de um disco corromper os dados em silêncio. Na prática pode tratá-lo como único; a matemática não é o elo fraco.

Versão 4 contra versão 7 — a decisão que importa

A versão 4 é aleatória de ponta a ponta. Não transporta informação nenhuma: nem quando foi criada, nem por quem, nem em que ordem. Isso é a sua maior força ou o seu defeito central, conforme o lugar onde a colocar.

A versão 7, normalizada no RFC 9562 em 2024, substitui os primeiros 48 bits por uma marca temporal Unix em milissegundos e preenche o resto com aleatoriedade. Como o tempo vem primeiro e o valor se lê da esquerda para a direita, ordenar identificadores v7 como texto simples também os ordena cronologicamente.

Não é uma diferença cosmética. Os bancos de dados armazenam as chaves primárias num índice B-tree ordenado. Insira chaves v7 e cada linha nova aterra no extremo direito da árvore, ao lado da anterior — as páginas onde escreve mantêm-se em memória e o índice cresce arrumado. Insira chaves v4 e cada escrita cai numa posição aleatória, o banco de dados toca numa página diferente de cada vez, a taxa de acertos na cache desce e o índice fragmenta-se. Numa tabela grande e movimentada a diferença no débito de inserções é substancial, e é por isso que v7 foi adotado tão depressa para chaves primárias.

  • Escolha v7 para chaves primárias, identificadores de eventos e registros, e tudo o que quiser ordenar ou consultar por intervalo de data de criação.
  • Escolha v4 quando o identificador aparecer em um lugar não fidedigno e não puder deixar escapar nada — incluindo o fato de um registro ter sido criado mesmo antes de outro.
  • Qualquer uma serve para um ID de correlação, uma chave de idempotência ou um nome de arquivo, onde a ordem não interessa.

A contrapartida é exatamente essa fuga: um identificador v7 diz a quem o vir, ao milissegundo, quando foi criado. Para o ID de uma linha isso é normalmente inofensivo e muitas vezes útil. Para um token de reposição de senha ou um link público de compartilhamento é informação que não pretendia publicar — e esses não deviam ser UUID de forma alguma, mas tokens aleatórios de um gerador de segredos dedicado.

A ordem dentro do mesmo milissegundo

Uma sutileza que pega muitas implementações de v7: o hardware moderno gera muito mais do que um identificador por milissegundo. Se dois valores compartilharem a marca temporal, a sua ordem é decidida pelos bits aleatórios que se seguem — ou seja, ao acaso. Gere cinquenta num ciclo apertado e obtém cinquenta valores apenas aproximadamente ordenados, perdendo precisamente a propriedade pela qual escolheu v7.

O RFC 9562 resolve isto com um contador monótono, e esta ferramenta implementa-o: os 12 bits imediatamente a seguir à marca temporal contam para cima dentro de um milissegundo, portanto um lote é estritamente crescente e não apenas aproximado. Se o contador encher — mais de 4096 valores no mesmo milissegundo — o gerador pede emprestado o milissegundo seguinte em vez de dar a volta e emitir um valor que ordene antes do anterior. O relógio andando para trás, o que acontece com correções NTP, é tratado da mesma maneira.

Lendo um valor v7 da esquerda para a direita, tomando 0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b como exemplo:

  • 0190a1b2-c3d4 — a marca temporal Unix de 48 bits em milissegundos. Como vem primeiro, a ordem do texto é a ordem do tempo.
  • 7 — o dígito de versão, que é o que torna isto um v7 e não um v4.
  • e5f — o contador monótono de 12 bits, que incrementa dentro de um mesmo milissegundo.
  • 8 — os bits de variante, fixados pelo RFC 9562 para todos os UUID modernos (sempre 8, 9, a ou b).
  • a9b-0c1d2e3f4a5b — os restantes 62 bits, puramente aleatórios.

Armazenar e usar UUID

A forma canónica é em minúsculas com hífenes, e o RFC 9562 diz que os geradores devem emitir exatamente isso — razão pela qual esta ferramenta não oferece opções de formato. Ainda assim vai encontrar outras grafias: em maiúsculas e entre chaves nas ferramentas da Microsoft, e sem hífenes onde alguém quis uma coluna mais curta. São todos os mesmos 128 bits, e as comparações devem ignorar maiúsculas e minúsculas.

Onde isto conta mesmo é no armazenamento. Um UUID tem 16 bytes, mas a forma textual tem 36 caracteres — por isso armazená-lo como texto mais do que duplica o espaço na linha e, mais importante, em todos os índices que o incluam. Use um tipo nativo onde exista um:

uuid                      -- PostgreSQL: tipo nativo de 16 bytes
BINARY(16)                -- MySQL: compacto; CHAR(36) desperdiça 20 bytes/linha
uniqueidentifier          -- SQL Server
crypto.randomUUID()       // JavaScript: só v4, exige contexto seguro
uuid.uuid4() / uuid7()    # Python: v4 na biblioteca padrão; v7 por biblioteca

Um último cuidado: um UUID identifica, não autoriza. Como não podem ser adivinhados, é tentador tratar como privado um URL não listado que contenha um. Mas os identificadores escapam — por registros, histórico do navegador, cabeçalhos referrer e capturas de tela — por isso tudo o que precisa mesmo de proteção continua precisando de uma verificação de permissões a sério por trás.

Como saber se um UUID é v4 ou v7

Normalmente um identificador chega às suas mãos sem nenhuma nota a dizer de onde veio, e não é preciso nenhuma: a versão está escrita dentro do próprio valor. Ambos são 32 dígitos hexadecimais no mesmo agrupamento 8-4-4-4-12, por isso a diferença não está na forma — está em dois dígitos isolados em posições fixas, e todo o resto decorre deles.

  • O primeiro dígito do terceiro grupo é o dígito de versão. Um 4 ali significa versão 4, um 7 significa versão 7, e nenhuma outra parte do valor tem voto nisso.
  • O primeiro dígito do quarto grupo carrega os bits de variante e vale 8, 9, a ou b nas duas versões — por isso nunca diz a você qual das duas está em suas mãos. O que diz é que o valor segue mesmo o RFC 9562; qualquer outra coisa nessa posição é uma disposição mais antiga, ou não é um UUID.
  • Se o dígito de versão for 7, os primeiros doze dígitos são a hora de criação: uma contagem de 48 bits de milissegundos desde o início de 1970, escrita em hexadecimal.
  • Se for 4, não há mais nada a ler. Um v4 não codifica hora, nem máquina, nem ordem, que é exatamente a propriedade pela qual foi escolhido.

Pegue o valor que a seção acima decompõe, 0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b. O terceiro grupo dele abre com um 7, portanto é uma versão 7, e os primeiros doze dígitos são 0190a1b2c3d4 — converta esse número hexadecimal para decimal, entregue-o ao Conversor de timestamp Unix e você obtém uma tarde de julho de 2024. Ao lado dele, 9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d abre o terceiro grupo com um 4, e os seus próprios doze primeiros dígitos são dados aleatórios que não decodificam nada. Os dois concordam em exatamente uma coisa: ambos abrem o quarto grupo com um dos quatro dígitos que os bits de variante permitem.

O v7 resiste a colisões tanto quanto o v4?

É uma dúvida justa, porque o v7 leva mesmo menos aleatoriedade. A versão 4 gasta 122 dos seus 128 bits em dados aleatórios. A versão 7 gasta 48 na marca temporal e, aqui, mais 12 no contador monótono, deixando 62 bits aleatórios — pouco mais de metade. Lido como número nu, isso parece um recuo sério.

  • Dentro de um único lote desta ferramenta uma repetição não é improvável, é impossível. Os valores que compartilham um milissegundo recebem contadores diferentes, e os que não compartilham milissegundo levam marcas temporais diferentes, por isso não pode haver dois iguais — é aritmética, não probabilidade.
  • Entre duas máquinas que geram no mesmo instante, as probabilidades são no máximo o problema dos aniversários sobre 62 bits, que fica equilibrado mais ou menos na raiz quadrada do espaço: na ordem dos dois bilhões de valores, e todos num único e mesmo milissegundo.
  • Entre milissegundos diferentes uma colisão de v7 não é só improvável, é impossível, porque os próprios dígitos iniciais já diferem.

Portanto a comparação honesta não é 122 bits contra 62. É uma loteria sorteada repetidamente durante toda a vida do sistema contra uma loteria separada e muito menor, realizada dentro de cada milissegundo e jogada fora quando o milissegundo acaba. O segundo arranjo é o mais forte, e a razão para preferir o v4 continua a ser a que a seção da comparação dá — não as colisões, mas o fato de um v7 dizer em voz alta quando foi criado.

Passar uma tabela existente de v4 para v7

A pergunta que se segue à escolha do v7 é o que fazer às linhas que já estão na tabela, e a parte tranquilizadora é que a coluna não precisa mudar nada. Ambas as versões são os mesmos 128 bits na mesma forma textual de 36 caracteres, por isso uma coluna uuid do PostgreSQL, um BINARY(16) ou um CHAR(36) aceitam uma mistura sem perceber. Você começa a gerar v7 para as linhas novas e para por aí: não há passo de migração nem preenchimento retroativo.

  • O que se ganha de imediato: toda linha inserida a partir de agora leva uma marca temporal à cabeça, por isso as chaves novas aterram lado a lado numa das pontas do índice em vez de se espalharem por ele. Esse ganho é sobre para onde vão as escritas novas, e está lá desde a primeira inserção.
  • O que nunca chega: as linhas que já lá estão ficam sem ordem para sempre. Nada consegue pôr uma hora de criação num valor a que ela nunca foi dada, e reemitir todos os identificadores significa reescrever todas as chaves estrangeiras que apontam para eles — um trabalho muito maior do que trocar de gerador, e que raramente compensa só pelo índice.
  • O que vigiar: ordenar a coluna deixa de querer dizer uma única coisa. Uma contagem de milissegundos é um número pequeno para um campo de 48 bits, por isso as chaves v7 se juntam numa faixa estreita na parte baixa do intervalo enquanto as v4 estão espalhadas por ele todo, e de vez em quando uma delas cai lá no meio.

É este último ponto que morde, porque uma consulta que ordena pela chave parece certa sobre os dados novos e relata os antigos errado, em silêncio. Se você precisa de uma única ordem para a tabela toda, acrescente uma coluna com a hora de criação e ordene por ela, e deixe o identificador voltar a ser um identificador. Pelo menos a mistura continua legível entretanto: o dígito de versão está em cada valor, por isso uma consulta consegue separar as duas épocas sem uma segunda coluna quando é preciso.

Perguntas frequentes

Devo usar v4 ou v7?
Use v7 para chaves primárias e tudo o que for ordenar por data de criação: a marca temporal à cabeça mantém as inserções agrupadas no fim do índice em vez de espalhadas por ele. Use v4 quando o identificador não puder revelar nada, nem sequer quando foi criado.
Dois UUID podem ser iguais?
É possível mas extraordinariamente improvável. A versão 4 tem 122 bits aleatórios, por isso mesmo depois de gerar bilhões de valores a probabilidade de colisão continua muito abaixo da de o armazenamento os corromper em silêncio.
O que aconteceu às versões 1, 3 e 5?
A versão 1 codifica uma marca temporal e o endereço MAC da máquina, o que revela a identidade do hardware e nem sequer pode ser produzido num navegador. As versões 3 e 5 derivam um UUID de forma determinística a partir de um espaço de nomes e um nome usando MD5 ou SHA-1 — útil quando a mesma entrada tem de dar sempre o mesmo identificador, mas é um trabalho diferente de gerar um novo.
Um UUID é suficientemente seguro como token secreto?
Um UUID v4 não pode ser adivinhado, mas um v7 codifica abertamente a sua hora de criação, e nenhum deles foi pensado como credencial. Para reposições de senha, tokens de sessão ou links de compartilhamento, gere um segredo aleatório dedicado e verifique permissões no servidor em vez de confiar em que o identificador é difícil de adivinhar.
Por que o meu lote de v7 não fica perfeitamente ordenado em outras ferramentas?
Porque muitas implementações saltam o contador monótono. Quando vários valores compartilham um milissegundo, a ordem fica entregue aos bits aleatórios seguintes. Esta ferramenta implementa o contador do RFC 9562, por isso um lote gerado aqui é estritamente crescente.
Como devo armazenar um UUID num banco de dados?
Num tipo nativo de 16 bytes onde exista — uuid no PostgreSQL, uniqueidentifier no SQL Server, BINARY(16) no MySQL. Armazenar em vez disso a forma textual de 36 caracteres mais do que duplica o espaço usado na linha e em cada índice que inclua a coluna.
São gerados nos vossos servidores?
Não. São gerados no seu navegador com crypto.getRandomValues, a fonte criptográfica de aleatoriedade da plataforma — a mesma que o crypto.randomUUID usa. Nenhum valor é enviado para lugar nenhum, e recarregar a página produz um conjunto inteiramente novo.
Dá para saber quando um UUID v7 foi criado?
Dá, e não precisa de mais nada além do valor. Os seus doze primeiros dígitos hexadecimais são uma contagem de milissegundos desde o início de 1970: converta-os para decimal e entregue o resultado ao Conversor de timestamp Unix. Um v4 não tem esse campo, por isso os mesmos doze dígitos ali são aleatórios e não decodificam nada.
O v7 tem menos bits aleatórios do que o v4?
Tem — 62 aqui contra os 122 do v4, porque a marca temporal e o contador ocupam o espaço. Na prática isso não torna uma colisão mais provável: dois valores v7 só podem colidir se tiverem sido criados no mesmo milissegundo, por isso esses 62 bits são gastos dentro de um milissegundo e não ao longo de toda a vida do sistema, e dentro de um único lote desta ferramenta o contador torna uma repetição impossível em vez de apenas improvável.
Podem UUID v4 e v7 conviver na mesma coluna?
Podem. São os mesmos 128 bits na mesma forma textual, por isso nada muda no esquema e não há migração — gera v7 a partir de agora e deixa as linhas antigas em paz. A única coisa a vigiar é a ordenação: as linhas novas ficam entre si por ordem de criação, mas as antigas e aleatórias estão espalhadas à volta delas, por isso ordenar pela chave não é uma ordem temporal para a tabela no seu conjunto.
O que são as versões 6 e 8 de UUID?
O RFC 9562 define ambas ao lado do v7. A versão 6 é a versão 1 com os campos da marca temporal reordenados para que o valor fique ordenado cronologicamente, e é destinada a sistemas já comprometidos com o v1 — o RFC diz que todo o resto deve usar v7. A versão 8 é um espaço deliberadamente aberto para disposições próprias, no qual só ficam fixos os bits de versão e de variante e os 122 restantes você define. Esta ferramenta não gera nenhuma das duas.

Ferramentas relacionadas