UUID vs GUID: o que realmente muda

GUID é o nome que a Microsoft dá ao UUID. As diferenças que aparecem na prática são a formatação do texto, a ordem dos bytes quando o valor é guardado em 16 bytes e a ordenação do SQL Server.

Se você só precisa da resposta curta: GUID e UUID são o mesmo tipo de valor, um identificador de 128 bits normalmente escrito como 32 dígitos hexadecimais em cinco grupos. Guid.NewGuid() no .NET e crypto.randomUUID() no JavaScript produzem, ambos, UUIDs aleatórios de versão 4. Mesmo assim, problemas de interoperabilidade acontecem, e eles vêm de três lugares: como o valor é escrito como texto, em que ordem ficam os seus 16 bytes e como o SQL Server o compara.

Dois nomes para um único padrão

Os UUIDs vêm do Network Computing System da Apollo e foram levados para o Distributed Computing Environment (DCE) da OSF. A Microsoft adotou o projeto do DCE no COM e chamou os valores de globally unique identifiers, os GUIDs. Mais tarde, o IETF publicou o formato como RFC 4122, em julho de 2005, substituída em maio de 2024 pela RFC 9562, a versão que acrescentou os UUIDs v6, v7 e v8. As duas RFCs dizem com todas as letras que UUIDs também são conhecidos como GUIDs.

Um campo guarda essa história de nomes. Os bits de variante no início do quarto grupo distinguem os UUIDs da RFC, em que esse grupo começa com 8, 9, a ou b, de uma faixa reservada para compatibilidade retroativa da Microsoft, que começa com c ou d. Os GUIDs criados pelas versões atuais do Windows e do .NET usam a variante da RFC, então na prática os dois são indistinguíveis.

Formatos de texto

O Guid.ToString() do .NET aceita um especificador de formato. Estas são as saídas para o mesmo valor no .NET 8:

EspecificadorSaída
Nf47ac10b58cc4372a5670e02b2c3d479
D (padrão)f47ac10b-58cc-4372-a567-0e02b2c3d479
B{f47ac10b-58cc-4372-a567-0e02b2c3d479}
P(f47ac10b-58cc-4372-a567-0e02b2c3d479)
X{0xf47ac10b,0x58cc,0x4372,{0xa5,0x67,0x0e,0x02,0xb2,0xc3,0xd4,0x79}}

Todas estão em minúsculas. O estilo em maiúsculas com chaves, {F47AC10B-58CC-4372-A567-0E02B2C3D479}, vem do Registro do Windows, dos IDs de classe do COM e dos arquivos de projeto do Visual Studio, e o SQL Server também exibe valores uniqueidentifier em maiúsculas. A RFC 4122 pede saída em minúsculas e entrada sem distinção entre maiúsculas e minúsculas, e o Guid.Parse aceita todas as formas da tabela.

A regra prática: nunca compare GUIDs como strings. "{F47AC10B-...}" e "f47ac10b-..." são strings diferentes e o mesmo valor. Faça o parse dos dois lados, ou normalize para minúsculas e sem chaves antes de guardar ou comparar.

Ordem dos bytes: a diferença que causa bugs

Guardado em 16 bytes, um UUID da RFC é big-endian: os bytes vêm na mesma ordem dos dígitos hexadecimais. A estrutura GUID da Microsoft é diferente. Ela é declarada como um inteiro de 32 bits, dois inteiros de 16 bits e 8 bytes avulsos, e em máquinas little-endian os três inteiros são guardados com os bytes invertidos. Guid.ToByteArray() devolve exatamente esse layout, e new Guid(byte[]) espera recebê-lo:

Text:           f47ac10b-58cc-4372-a567-0e02b2c3d479
RFC bytes:      f4 7a c1 0b  58 cc  43 72  a5 67  0e 02 b2 c3 d4 79
ToByteArray():  0b c1 7a f4  cc 58  72 43  a5 67  0e 02 b2 c3 d4 79

Os últimos 8 bytes são idênticos; os três primeiros grupos estão invertidos. O SQL Server usa o mesmo layout misto: converter um uniqueidentifier para binary(16) dá os mesmos bytes de ToByteArray().

O bug aparece quando um lado grava os bytes em um layout e o outro lado os lê no outro. Um serviço .NET grava ToByteArray() em uma coluna BINARY(16) ou em uma mensagem binária; um serviço em Java, Go ou Python lê os bytes como um UUID da RFC e obtém 0bc17af4-cc58-7243-a567-0e02b2c3d479. O valor continua único, então nada falha, mas as buscas entre os dois sistemas nunca encontram correspondência. O dígito de versão também mudou de lugar: neste exemplo, o valor embaralhado diz ser da versão 7.

Escolha um layout e converta explicitamente na fronteira entre os sistemas:

  • .NET 8 e posteriores: guid.ToByteArray(bigEndian: true) e new Guid(bytes, bigEndian: true) leem e gravam na ordem da RFC.
  • Python: uuid.UUID(bytes_le=data) lê o layout da Microsoft, e u.bytes_le o grava.
  • Em qualquer lugar: se você guarda texto ou um tipo UUID nativo em vez de bytes brutos, a questão nem chega a aparecer.

Como o SQL Server ordena uniqueidentifier

O SQL Server não ordena GUIDs do jeito que o texto sugere. Ele compara primeiro o último grupo, de seis bytes, depois o quarto grupo, e em seguida o terceiro, o segundo e o primeiro, e dentro desses três primeiros grupos o byte mais à direita é o que mais pesa. A lista abaixo está em ordem crescente de uniqueidentifier, conforme produzida pelo tipo SqlGuid do .NET, que implementa a mesma comparação:

01000000-0000-0000-0000-000000000000
ff000000-0000-0000-0000-000000000000
00000001-0000-0000-0000-000000000000
00000000-0100-0000-0000-000000000000
00000000-0001-0000-0000-000000000000
00000000-0000-0100-0000-000000000000
00000000-0000-0001-0000-000000000000
00000000-0000-0000-0001-000000000000
00000000-0000-0000-0100-000000000000
00000000-0000-0000-0000-000000000002
00000000-0000-0000-0000-010000000000

Ordenar os mesmos valores como System.Guid no .NET, ou como strings em qualquer linguagem, dá outra ordem: 00000000-0000-0000-0000-000000000002 vem primeiro e ff000000-0000-0000-0000-000000000000 por último. Isso tem duas consequências:

  • A paginação por keyset pode pular ou repetir linhas se a aplicação ordena os GUIDs por conta própria e depois pede ao SQL Server as linhas WHERE id > @last. Deixe o banco de dados fazer as duas coisas.
  • O UUID v7 não ajuda no SQL Server. Um valor v7 começa com um timestamp, mas o SQL Server compara primeiro o final aleatório, então chaves v7, incluindo as do Guid.CreateVersion7() do .NET 9, caem em posições aleatórias de um índice uniqueidentifier, exatamente como chaves v4.

NEWID vs NEWSEQUENTIALID

NEWID()NEWSEQUENTIALID()
Como é geradoAleatório, versão 4UuidCreateSequential do Windows, um UUID baseado em tempo, com os bytes reorganizados
Onde pode ser usadoEm qualquer ponto do T-SQLSó em uma restrição DEFAULT de uma coluna uniqueidentifier
OrdemAleatóriaCrescente na ordenação do SQL Server naquela máquina; pode recomeçar de um valor menor depois que o Windows reinicia
PrevisívelNãoSim: o próximo valor pode ser adivinhado
Comportamento no índiceOs inserts caem em qualquer lugar e causam divisões de páginaOs inserts vão para o final do índice

O NEWSEQUENTIALID() existe porque chaves aleatórias fragmentam índices clusterizados. A reorganização dos bytes é o que faz os seus valores crescerem sob a comparação incomum do SQL Server, e é também por isso que ordená-los como texto não reproduz a ordem em que foram criados. Como os valores podem ser adivinhados, a documentação da Microsoft desaconselha o seu uso quando a privacidade importa; mantenha esses IDs fora das URLs. Se as chaves forem geradas na aplicação, ordenar como o SQL Server exige colocar os bytes baseados em tempo no último grupo, o oposto do que o UUID v7 faz.

Gerar um ou outro

Como os valores são os mesmos, qualquer gerador de UUID v4 é um gerador de GUID. O gerador de GUID produz valores aleatórios de versão 4 no estilo do Registro, em maiúsculas e com chaves, e o gerador de UUID v4 produz o mesmo tipo de valor em minúsculas, no formato da RFC. Os dois permitem ligar ou desligar chaves, maiúsculas e hífens, o que cobre os formatos N, D e B mostrados acima.

Perguntas Frequentes

GUID é a mesma coisa que UUID?

Sim. GUID é o nome que a Microsoft usa para o formato UUID. Os dois são valores de 128 bits, e Guid.NewGuid() produz um UUID aleatório padrão de versão 4.

Por que o meu GUID fica diferente depois de lido do banco de dados?

Os três primeiros grupos estão com os bytes trocados. O ToByteArray() do .NET e o SQL Server guardam esses grupos em ordem little-endian, enquanto a maioria das outras linguagens lê 16 bytes na ordem big-endian da RFC. Converta explicitamente, ou guarde o valor como texto ou em um tipo UUID nativo.

GUIDs devem ficar em maiúsculas ou minúsculas?

As duas formas são válidas. A RFC 4122 recomenda saída em minúsculas e entrada sem distinção entre maiúsculas e minúsculas; ferramentas do Windows e o SQL Server exibem em maiúsculas. Compare valores já interpretados, e não strings.

O UUID v7 mantém os índices do SQL Server em ordem?

Não. O SQL Server compara primeiro os últimos seis bytes de um uniqueidentifier, e em um valor v7 esses bytes são aleatórios. O NEWSEQUENTIALID() é ordenado para o SQL Server; o v7 é ordenado para bancos de dados que comparam os bytes da esquerda para a direita.