UUID and ULID in .NET: Maximizing Efficiency in Unique Identifiers

Merwan Chinta
CodeNx
Published in
3 min readJun 23, 2024

Unique identifiers are fundamental in software development, particularly in distributed systems where ensuring uniqueness across multiple nodes is critical.

While UUIDs (Universally Unique Identifiers) have been the industry standard for years, ULIDs (Universally Unique Lexicographically Sortable Identifiers) are emerging as a superior alternative in certain contexts.

This article will delve into the technical intricacies of UUIDs and ULIDs, their practical applications, and how to implement them in .NET with a focus on performance and scalability.

Dive into UUIDs

UUIDs, or GUIDs (Globally Unique Identifiers) in .NET, are 128-bit numbers designed to be globally unique. The standard format for a UUID is:

afdf5738–6a8e-4d1a-90db-e894a3828320

UUID Structure

  • Version: The 13th character in a UUID indicates its version (e.g., version 1 is time-based, version 4 is random).
  • Variant: The 17th character indicates the variant, defining the UUID layout and meaning.

UUID Generation in .NET

To generate a UUID in .NET, the Guid class is used:

Guid newGuid = Guid.NewGuid();
Console.WriteLine(newGuid.ToString());

Performance Considerations with UUIDs

UUIDs, especially version 4, are highly random. This randomness, while ensuring uniqueness, can lead to performance bottlenecks in databases due to poor indexing efficiency. Sequential UUIDs (e.g., version 1) mitigate this to some extent but can expose timestamp information, which may be undesirable.

Introduction to ULIDs

ULIDs address some of the performance and sorting limitations of UUIDs. They combine a timestamp with random data to create a 128-bit identifier that is lexicographically sortable. A typical ULID looks like this:

01HZW2RKF63AWC1BT901FPGRGT

Ulid Sturcture explained — Image source here

ULID Structure

  1. Timestamp: The first 10 characters encode a Unix timestamp in milliseconds.
  2. Randomness: The remaining characters are randomly generated, ensuring uniqueness.

Generating ULIDs in .NET

First, install the Ulid package via NuGet, and generate:

dotnet add package Ulid
using System;
using NUlid;

class Program
{
static void Main()
{
Ulid newUlid = Ulid.NewUlid();
Console.WriteLine(newUlid.ToString());
}
}

Advantages of ULIDs

  • Lexicographical Sorting: ULIDs are designed to be lexicographically sortable, which significantly enhances database indexing and querying performance.
  • Compatibility: ULIDs are 128-bit, ensuring compatibility with systems designed for UUIDs.

Use Cases and Performance Optimization

Database Indexing

Using ULIDs in databases can vastly improve indexing efficiency. Since ULIDs are sortable by their timestamp component, they maintain insertion order, reducing fragmentation and improving read and write performance.

Example: Entity Framework Integration

To leverage ULIDs in Entity Framework, you can configure your models to use ULIDs as primary keys:

public class Order
{
[Key]
public Ulid OrderId { get; set; }

public DateTime OrderDate { get; set; }

// Other properties
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>()
.Property(o => o.OrderId)
.HasConversion(
v => v.ToString(),
v => Ulid.Parse(v));
}

Distributed Systems

In distributed systems, the unique and sortable nature of ULIDs makes them ideal for scenarios where chronological order is important, such as logging and event sourcing.

Example: Implementing ULIDs in a Distributed Context

public class DistributedEvent
{
public Ulid EventId { get; set; }

public string EventData { get; set; }

public DateTimeOffset Timestamp { get; set; }
}

public class EventService
{
public void LogEvent(string eventData)
{
var distributedEvent = new DistributedEvent
{
EventId = Ulid.NewUlid(),
EventData = eventData,
Timestamp = DateTimeOffset.UtcNow
};

// Save to database or send to event queue
}
}

While UUIDs have served as a robust solution for generating unique identifiers, their limitations in terms of randomness and indexing inefficiency can be significant in high-performance applications. ULIDs provide a compelling alternative, offering the same level of uniqueness with additional benefits like lexicographical sorting and improved readability. You can enhance your application’s performance, scalability, and maintainability.

Next Steps

  • Evaluate Your Needs: Assess whether the additional benefits of ULIDs align with your application’s requirements.
  • Benchmark: Perform benchmarks in your specific environment to measure the impact of using ULIDs versus UUIDs.
  • Implement: Start integrating ULIDs into your .NET applications where appropriate, particularly in areas where indexing performance is critical.

I trust this information has been valuable to you. 🌟 Wishing you an enjoyable and enriching learning journey!

📚 For more insights like these, feel free to 👏 follow 👉 Merwan Chinta

--

--

Merwan Chinta
CodeNx

🚧 Roadblock Eliminator & Learning Advocate 🖥️ Software Architect 🚀 Efficiency & Performance Guide 🌐 Cloud Tech Specialist