.NET  

Understanding GUID, UUID, and ULID in C#

Introduction

When designing distributed systems and databases, ensuring the uniqueness of identifiers across systems, sessions, or records is a critical task. GUID (Globally Unique Identifier), UUID (Universally Unique Identifier), and ULID (Universally Unique Lexicographically Sortable Identifier) are popular solutions for generating unique IDs.

In this article, you'll discover.

  1. What GUID, UUID, and ULID are,
  2. Their differences and use cases,
  3. How to implement them in C#, and
  4. A performance-oriented analysis of their suitability for specific applications.

What are GUID, UUID, and ULID?

1. GUID (Globally Unique Identifier)

A GUID is a 128-bit unique identifier widely used in Microsoft-based systems. While GUID is a term Microsoft uses, it is essentially a UUID (as specified in RFC 4122). By nature, GUIDs guarantee uniqueness across distributed systems by relying on either randomness or timestamp-based generation strategies.

Format: GUIDs are typically represented as a 36-character hexadecimal string with hyphens.

XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

Example: f47ac10b-58cc-4372-a567-0e02b2c3d479

Key Properties

  • Uniqueness: Globally unique across systems.
  • Randomness: Commonly generated with UUID v4 (random generation).

Primary Use Cases

  • Unique Primary Keys in distributed databases.
  • Globally unique API keys, session tokens, and resource identifiers.
  • Identification of components/resources in Microsoft technologies like COM (.NET Class IDs).

2. UUID (Universally Unique Identifier)

A UUID is an international standard for generating unique 128-bit identifiers, defined under RFC 4122. Conceptually, GUID and UUID are nearly identical, though UUID strictly follows the RFC and is widely used across non-Microsoft frameworks.

Format: UUID adopts the same format as GUID.

XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

Example: 550e8400-e29b-41d4-a716-446655440000

Key Properties

  • Cross-platform Compatibility: Supported by programming languages like Python, Java, Go, and Node.js.
  • Determinism: Using UUID v3/v5 allows deterministic generation for the same inputs.

Primary Use Cases

  • Unique IDs in distributed services and databases.
  • Assigning identifiers to resources in cross-platform systems.
  • APIs and microservices need universal identifiers.

3. ULID (Universally Unique Lexicographically Sortable Identifier)

A ULID is a 128-bit identifier designed to overcome the shortcomings of GUID/UUID in sorting scenarios. ULIDs embed timestamp information into the identifier, making them lexicographically sortable (i.e., they can be sorted naturally based on their textual representations).

Format: Base32-encoded string without special characters.

01GZHT44KMWWT5V2Q4RQ6P8VWT
  • First 48 bits: Millisecond timestamp (ensures natural ordering).
  • Last 80 bits: Random entropy.

Key Properties

  • Ordered: Unlike GUID/UUID, ULIDs are naturally sortable because the timestamp is embedded upfront.
  • Readable: Fewer characters than UUID (Base32 encoding instead of Base16/Hex).

Primary Use Cases

  • Log and Event Tracking: Create lexicographically ordered event logs.
  • High-Frequency Inserts: Reduce database index fragmentation compared to GUIDs/UUIDs.
  • Human-readable, unique IDs.

Comparing GUID, UUID, and ULID: Features and Suitability

Aspect GUID UUID ULID
Bit Size 128 bits 128 bits 128 bits
Encoding Hexadecimal Hexadecimal Base32
Sorting Not Sortable Not Sortable Lexicographically Sortable
Contains Timestamp? Optional Optional Yes
Write Performance High Fragmentation in DB Index High Fragmentation in DB Index Low Fragmentation (Sequential IDs)
Primary Use Microsoft systems Cross-platform, API, databases Logging, time-ordered systems

Use Cases for GUID, UUID, and ULID

When to Use GUID

  • Microsoft Ecosystems: COM components, .NET assemblies, and Azure Services utilize GUIDs extensively.
  • Distributed Databases: Ensures unique keys even when records are written independently across systems.
  • Session Tracking: Use a GUID to assign globally unique session IDs.

When to Use UUID

  • Cross-Platform Compatibility: Works across distributed applications and languages like Python, Java, .NET.
  • APIs and Microservices: Generate identifiers for resources shared across multiple systems.
  • Randomized Unique IDs: UUID v4 is ideal for cases requiring uniqueness without predictable patterns.

When to Use ULID

Logging Systems: Generate sortable, unique IDs to track events or logs while maintaining a time correlation.

Performance Analysis for Databases

Database Indexing

  • GUID/UUID: IDs generated randomly (e.g., v4) lead to non-sequential inserts in clustered indexes, resulting in index fragmentation.
  • ULID: ULID's sequential nature (timestamp) ensures that inserts are ordered naturally, reducing index fragmentation.

Example Performance Metrics (MySQL/PostgreSQL)

Metric GUID/UUID ULID
Insert Speed Slower (Random Inserts) Faster (Sequential Inserts)
Index Fragmentation High Low
Query Performance Moderate Better
Storage 16 bytes per ID 16 bytes per ID

Implementing GUID, UUID, and ULID in C#

1. GUID in C#

The System.Guid class provides built-in support for creating GUIDs.

using System;

class Program
{
    static void Main()
    {
        Guid guid = Guid.NewGuid();
        Console.WriteLine($"Generated GUID: {guid}");
    }
}

2. UUID in C#

In .NET Core and Framework, Guid is already a UUID generator (v4 by default).

using System;

class Program
{
    static void Main()
    {
        // Generate a UUID (same as GUID)
        Guid uuid = Guid.NewGuid();
        Console.WriteLine($"Generated UUID (v4): {uuid}");
    }
}

3. ULID in C#

The .NET ecosystem does not natively support ULIDs, but several libraries like Ulid can be used.

Steps

  1. Install UlidSharp from NuGet
    dotnet add package Ulid
  2. Generate ULID
    class Program
    {
        static void Main()
        {
            // Generate a ULID
            var ulid = Ulid.NewUlid();
            Console.WriteLine($"Generated ULID: {ulid}");
        }
    }

Test Samples

Test Samples

Best Practices

  • GUID/UUID
    • Avoid using GUIDs/UUIDs as a primary key in clustered indexes unless uniqueness is more critical than performance.
    • Where possible, consider surrogate keys for better performance.
  • ULID
    • Use ULIDs in time-sensitive applications or logs.
    • Ideal for high-concurrency, high-volume systems like IoT devices or analytics workloads.

Conclusion

GUID, UUID, and ULID each offer distinct advantages based on the application's requirements.

  • If you're developing in the Microsoft ecosystem or need universal uniqueness, GUIDs/UUIDs are excellent.
  • If your application requires ordering, low index fragmentation, and better performance for high-insert workloads, ULID is a superior option.

By understanding the strengths and limitations of each, you can optimize your systems for unique identifier performance, scalability, and order requirements!