Skip to main content

Command Palette

Search for a command to run...

UUID, ULID, NanoIDs and Snowflake IDs , What's the difference?

Updated
9 min read
UUID, ULID, NanoIDs and Snowflake IDs , What's the difference?

Hello everyone and welcome to the first article of 2025! In this article we’re going to be talking all about unique id generators. Different schemes that generate unique ids for a specific scope whether global or custom. Let’s start by discussing each one separately and having a pro con list at the end that might guide you to the answer of which one do I use? Let’s start

UUIDs

UUID stands for universal unique identifiers. They allow generating ids in a way that guarantees uniqueness without knowledge of other systems. They have many versions some are not used as much anymore let’s discuss each version

UUIDv1

A UUID version 1 is known as a time-based UUID and can be broken down as follows:

UUIDv1: [time_low]-[time_mid]-[time_high_and_version]-[clock_seq_and_reserved]-[node]

UUIDv1 uses a 60-bit timestamp that represents the number of 100-nanosecond intervals elapsed since the Gregorian epoch: 15 October 1582 00:00:00 UTC. This timestamp is used to ensure that UUIDs generated at different times are unique. e.g 13743895347200 represents the number of 100 nanosecond intervals passed since epoch. Using the nanosecond representation allows for more uniqueness and finer granularity. Using 100-nanosecond intervals allows for up to 10 million unique time intervals per second.

The total timestamp is 60 bits:

  • Most significant bits represent older times.

  • Least significant bits represent the finer granularity within the most recent time interval.

Timestamp and its 60-Bit Division in UUIDv1:

The 60-bit timestamp is divided across three fields in UUIDv1:

  1. Time Low: Stores the least significant 32 bits of the timestamp.

  2. Time Mid: Stores the next 16 bits.

  3. Time High: Stores the most significant 12 bits.

These bits are arranged in a specific format to construct the UUID.

Node is a 48-bit value, often derived from the MAC address of the machine generating the UUID. If the MAC address is unavailable, a random value is used instead.

The Clock sequence in UUID Version 1 (UUIDv1) is a 14-bit value that ensures uniqueness when generating UUIDs in situations where the system clock may not be reliable.

It helps maintain uniqueness when:

  • The system clock is adjusted backward.

  • The system clock cannot guarantee monotonicity (e.g., due to manual adjustments or hardware issues).

UUIDv2

In V2 the low_time segment of the structure was replaced with a POSIX user ID. The theory was that these UUIDs could be traced back to the user account that generated them. Since the low_time segment is where much of the variability of UUIDs reside, replacing this segment increases the chance of collision. As a result, this version of the UUID is rarely used.

UUIDv3 & UUID v5

Versions 3 and 5 of UUIDs are very similar. The goal of these versions is to allow UUIDs to be generated in a deterministic way so that, given the same information, the same UUID can be generated. These implementations use two pieces of information: a namespace (which itself is a UUID) and a name. These values are run through a hashing algorithm to generate a 128-bit value that can be represented as a UUID.

The key difference between these versions is that version 3 uses an MD5 hashing algorithm, and version 5 uses SHA1.

UUIDv4

Version 4 is known as the random variant because, as the name implies, the value of the UUID is almost entirely random. The exception to this is the first position in the third segment of the UUID, which will always be 4 to signify the version used.

UUIDv6

Version 6 is nearly identical to Version 1. The only difference is that the bits used to capture the timestamp are flipped, meaning the most significant portions of the timestamp are stored first.

[time_high_and_version]-[time_mid]-[time_low]-[clock_seq_and_reserved]-[node]

The main reason for this is because UUIDV1 had problems when it came to two types or sorting,

Lexicographical order is similar to how words are arranged in a dictionary or alphabetical order. It's based on the lexicographic (dictionary) comparison of characters or symbols in a string, from left to right.

Chronological order refers to sorting based on time — from the earliest to the latest (or vice versa). This is the kind of order you'd expect when sorting timestamps, dates, or events. In chronological order, items are compared based on their relative timing.

UUIDV1 is designed to be unique identifiers that can include a timestamp. However, its structure can cause confusion because lexicographical sorting does not always align with chronological sorting, due to how fields are ordered. e.g

UUID1: a1b2c3d4-e5f6-11ec-9abc-123456789abc
UUID2: a1b2c3d5-e5f6-11ec-9abc-123456789abc
  • Chronological order would expect UUID1 to come before UUID2 because UUID1 represents an earlier time.

  • Lexicographical order, however, would first compare time_low (the first field a1b2c3d4), and since a1b2c3d4 is less than a1b2c3d5, it might place UUID1 before UUID2this happens correctly, but in some cases, this alignment doesn’t hold true.

  • If the time_high_and_version field were placed at the start, then chronological sorting would naturally match lexicographical sorting, as in UUIDv6.

UUIDv7

Version 7 is also a time-based UUID variant, but it integrates the more commonly used Unix Epoch timestamp instead of the Gregorian calendar date used by Version 1. The other key difference is that the node (the value based on the system generating the UUID) is replaced with randomness, making these UUIDs less trackable back to their source.

Use Cases for UUIDs

  • Session Identifiers

  • File Storage and Versioning

  • API Tokens and Authentication

  • E-commerce and Order Tracking

  • Event Tracking and Logs

ULIDs

A ULID is a 128-bit identifier, represented as a 26-character string encoded in Base32 (with a specific alphabet). It has two main components:

  • Timestamp (48 bits or 6 bytes) it is the first component of a ULID and is stored in milliseconds since the UNIX epoch (1970-01-01 00:00:00 UTC). The timestamp is packed into the first 48 bits of the ULID, which allows it to be lexicographically sortable. This means ULIDs generated in chronological order will sort correctly without needing special sorting logic.

  • Randomness (80 bits or 10 bytes) to ensure that ULIDs are globally unique, the second part (after the timestamp) is made up of 80 random bits. This random component ensures that even if two ULIDs are generated at the exact same millisecond, they will still be distinct.

Example:

  • Base32 alphabet: A-Z, 2-7

  • ULID: 01FZQZ4E0AMK4NK9F7J8N9DAX8

Use Cases for ULIDs

  • Distributed systems: When you need globally unique IDs that are generated independently and can be sorted chronologically.

  • Database indexing: ULIDs can be used as primary keys because they are lexicographically sortable, reducing fragmentation and improving performance.

  • Caching: When creating time-sensitive keys or identifiers that need to be unique and sortable.

  • Also all UUID use cases can work here too.

UUID vs ULID

FeatureULIDUUID
Length26 characters36 characters (Hexadecimal)
EncodingBase32Hexadecimal (Base 16)
Timestamp48 bits (millisecond precision)60 bits (100-nanosecond precision)
SortableYes, lexicographically sortable by timeNo (UUIDv6 s partially sortable)
Randomness80 bits80 bits (in UUIDv4)
Use CaseIdeal for distributed systems and databasesGeneral purpose (e.g., unique identifiers)

NanoIDs

Nanoid is a small, secure, and URL-friendly unique identifier generator, typically consisting of a fixed-length string of random characters. It is much smaller in size compared to UUIDs and ULIDs, making it more efficient for use in contexts where shorter identifiers are needed. They do not contain timestamps and is completely random.

Key Characteristics of Nano IDs:

  • Length: A NanoID is around 21 characters long, but the length can be customized.

  • Alphabet: It uses a custom alphabet that avoids characters that might be confusing or problematic in URLs (like /, +, and =).

Nanoids are typically generated from a cryptographically secure random source, and the characters in the resulting identifier are drawn from a custom alphabet.

cryptographically secure random numbers (CSPRNGs) alone do not guarantee uniqueness. They ensure that the values generated are unpredictable and hard to reproduce, but they do not inherently ensure that each generated value is unique across all possible values.

The default alphabet used by Nanoid consists of the following 64 characters:

abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789

e.g V1StGXR8ZtM08l5c5yLyzLq

Snowflake IDs

Snowflake IDs are another form of unique identifier, popularized by Twitter and used in systems like Twitter’s distributed ID generator. They are designed to be unique, sortable, and compact, and are particularly well-suited for high-performance, distributed systems.

A typical Snowflake ID is a 64-bit integer, and the structure is usually broken down as follows:

  • Timestamp (41 Bit) Millisecond timestamp since epoch

  • Datacenter ID (5 Bit)

  • Worker/Node ID (5 bit)

  • Sequence (12 bits) Sequence number for handling multiple IDs generated in the same millisecond

  • total bits (64) which is the total size of the snowflake id.

Key Characteristics of Snowflake IDs:

  1. Time-based: The first 41 bits represent the timestamp, which makes Snowflake IDs chronologically sortable.

  2. Machine/Node-aware: Snowflake IDs include the datacenter and worker (node) IDs to avoid collisions in distributed environments.

  3. High throughput: With a 12-bit sequence number, Snowflake IDs can generate up to 4096 IDs per millisecond per machine, ensuring high throughput in distributed systems.

  4. Compact: The 64-bit integer format keeps the ID size compact, reducing storage and indexing overhead.

  5. Unique: The combination of timestamp, machine ID, and sequence number guarantees global uniqueness.

Best Use Cases for Snowflake IDs

  • High-Performance Distributed Systems

  • Event Sourcing

  • Scalable Web Applications

  • Logging and Monitoring Systems

A comparison between it and UUIDs

FeatureSnowflake IDUUID
Length64-bit integer (compact)128-bit (longer and bulkier)
StructureTime-based with machine/worker ID & sequenceCompletely random (UUIDv4), or timestamp-based (UUIDv1/6)
Time-sortableYes, naturally sortable by timestampNot time-sortable (except UUIDv1)
UniquenessGlobally unique (based on machine & sequence)Globally unique (UUIDv4 random or UUIDv1 timestamp)
PerformanceHigh throughput with up to 4096 IDs per msLower throughput (especially with UUIDv1)
CollisionsExtremely low, even in distributed systemsLow (but possible with UUIDv4 in high generation rate)
Use CaseHigh-throughput, distributed systemsGeneral use cases,, API tokens

Summary

In this article we went through the most popular unique identifier generation schemes listing the use cases for each and every one. If you’re planning on adding one I recommend understanding the main differences between them before making a decision because choosing a wrong unique identifier scheme in a big scale could affect performance in a negative way. That’s been it for this one see you in the next!

References

  1. https://planetscale.com/blog/the-problem-with-using-a-uuid-primary-key-in-mysql#uuidv4

  2. https://adileo.github.io/awesome-identifiers/

More from this blog

Hewi's Blog

65 posts