Introdução
Recentemente o ULID (Universally Unique Lexicographically Sortable Identifier) vem ganhando espaço, após o UUID (Famoso Guid no .net) e é sobre isso que quero tratar no artigo de hoje. Este artigo explora as diferenças entre UUIDs e ULIDs, incluindo benchmarks de desempenho, e como implementá-los em .NET.
Tradicionalmente, UUIDs (Universally Unique Identifiers) têm sido amplamente utilizados. No entanto, recentemente, ULIDs (Universally Unique Lexicographically Sortable Identifiers) têm ganhado popularidade devido às suas vantagens de desempenho, especialmente em sistemas de banco de dados. Vamos entender um pouco sobre o funcionamento de cada um dos dois:
UUID (Universally Unique Identifier)
UUID é um padrão de identificador que garante a unicidade. Eles são amplamente utilizados em sistemas distribuídos para garantir que os IDs gerados em diferentes sistemas não colidam.
Vantagens dos UUIDs:
- Unicidade Global: UUIDs garantem unicidade em escala global, sendo ideais para sistemas distribuídos.
- Padrão Amplamente Aceito: UUIDs são um padrão formal (RFC 4122), o que significa que têm suporte em diversas linguagens e plataformas.
- Versão 4 (UUIDv4): Totalmente aleatória, minimizando a possibilidade de colisões.
Desvantagens dos UUIDs:
- Desempenho em Bancos de Dados: UUIDs não são ordenados lexicograficamente, o que pode impactar negativamente o desempenho de gravações em banco de dados.
- Espaço de Armazenamento: O tamanho de 128 bits pode ser excessivo para alguns usos.
- Legibilidade: UUIDs podem ser difíceis de ler e manipular manualmente.
ULID (Universally Unique Lexicographically Sortable Identifier)
ULID é um formato de identificador que, além de garantir unicidade, é lexicograficamente ordenável e inclui um componente de timestamp.
Vantagens dos ULIDs:
- Ordenação Cronológica: ULIDs são ordenados cronologicamente, o que melhora o desempenho de gravações em banco de dados.
- Facilidade de Uso: São mais legíveis e fáceis de usar devido ao componente de timestamp.
- Desempenho: A ordenação cronológica reduz a fragmentação de páginas de memória em bancos de dados, melhorando a eficiência.
- URL Seguro: nenhum caractere especial significa que os IDs podem ser usados para compor URLs com segurança.
Desvantagens dos ULIDs:
- Implementação Variável: Diferente dos UUIDs, ULIDs não são um padrão formalizado, e as implementações podem variar.
- Compatibilidade: Pode não ser suportado por todas as linguagens e plataformas tão amplamente quanto UUIDs.
Motivadores para o Uso de ULIDs
- Desempenho de Banco de Dados: A ordenação cronológica dos ULIDs resulta em gravações mais eficientes em bancos de dados. Isso é especialmente importante em sistemas com alto volume de transações.
- Legibilidade e Usabilidade: ULIDs são mais legíveis e incluem um componente de timestamp, facilitando a depuração e análise.
- Redução de Fragmentação: Em bancos de dados, a fragmentação de páginas pode ser um problema sério. ULIDs ajudam a manter os dados mais compactos e ordenados, reduzindo a fragmentação.
Benchmarks de Desempenho
A Shopify reportou um aumento de 50% na taxa de gravação ao mudar uma de suas chaves MySQL de UUIDv4 para ULID. Este aumento de desempenho se deve ao fato de que UUIDv4 é totalmente aleatório, enquanto ULIDs são ordenados cronologicamente. Isso significa que as gravações recentes tendem a residir nas mesmas páginas de memória, melhorando significativamente o desempenho.
Estudos de Caso e Benchmarks:
- Shopify: Ao substituir UUIDv4 por ULID, a Shopify observou um aumento de 50% na taxa de gravação. A ordenação cronológica dos ULIDs significou que as requisições recentes permaneciam em páginas de memória contíguas, melhorando o desempenho.
- Outros Estudos: Em testes comparativos, ULIDs demonstraram uma redução significativa na fragmentação de páginas em bancos de dados como PostgreSQL e MySQL, resultando em melhorias de desempenho entre 20-50%, dependendo da carga de trabalho.
Exemplos em .NET
Abaixo fica um exemplo e comparativo de como gerar um UUID e um ULID em .net (8.0). Aqui estou utilizando a biblioteca Ulid (https://github.com/Cysharp/Ulid) para criar as chaves Ulid, ok?
using System;
using System.Diagnostics;
public class UUIDExample
{
public static void Main()
{
// Medir o tempo de geração do UUID
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Guid uuid = Guid.NewGuid();
stopwatch.Stop();
TimeSpan uuidTime = stopwatch.Elapsed;
Console.WriteLine("UUID: " + uuid.ToString());
Console.WriteLine("Tempo para gerar UUID: " + uuidTime.TotalMilliseconds + " ms");
// Medir o tempo de geração do ULID
stopwatch.Reset();
stopwatch.Start();
Ulid ulid = Ulid.NewUlid();
stopwatch.Stop();
TimeSpan ulidTime = stopwatch.Elapsed;
Console.WriteLine("ULID: " + ulid.ToString());
Console.WriteLine("Tempo para gerar ULID: " + ulidTime.TotalMilliseconds + " ms");
/*
Console Results:
UUID: 2dfcf7af-5507-4db6-a046-beca235cb554
Tempo para gerar UUID: 1.064 ms
ULID: 01J0PV7ZZ7FFM12SSPPRFP2E0W
Tempo para gerar ULID: 4.4684 ms
*/
}
}
Apesar de que ambas são muito rápidas, repare que a geração do Ulid é significantemente mais demorada para a geração, 1.064 ms vs 4.4684ms, porém o benchmark da shopfy não foi sobre a geração e sim sobre a inserção, o que é bom ficar registrado.
Deixei o reposítorio com o código fonte, neste link: https://github.com/jhomarolo/uuid-ulid
Conclusões
A escolha entre UUIDs e ULIDs depende, em última análise, dos requisitos específicos da sua aplicação. Se sua aplicação exigir identificadores exclusivos globalmente que possam ser gerados de forma rápida e eficiente, e você não precisar classificar ou pesquisar os identificadores, os UUIDs poderão ser a melhor escolha.
Por outro lado, se a sua aplicação exigir identificadores lexicograficamente classificáveis que possam ser gerados de forma rápida e eficiente, especialmente em ambientes de alto volume, os ULIDs poderão ser a melhor escolha. Os ULIDs também são mais eficientes em termos de espaço e seguros para URLs, o que pode ser uma consideração importante para alguns aplicativos.
Até a próxima!