Guide

How UUID v4 works: randomness and collisions

A Universally Unique Identifier (UUID) Version 4 is a 128-bit label designed to enable decentralized computing systems to assign global identities without coordinating with a central authority or database sequence generator. Unlike Version 1 and Version 2 UUIDs that rely on MAC addresses and physical timestamps, Version 4 derives its uniqueness entirely from pseudo-random numbers.

While developers frequently treat UUID v4 strings as purely random hexadecimal values, the specification (RFC 4122 and RFC 9562) reserves six fixed metadata bits. Understanding how those remaining 122 bits of randomness behave under the birthday paradox—and why unseeded random generators or B-tree indexing cause severe production pitfalls—is essential for robust distributed system architecture.

Open the UUID Generator

Use this when


  • Generating non-sequential public identifiers for API resources, customer orders, and tracking tokens
  • Creating primary keys in microservices architectures where databases cannot coordinate central autoincrement IDs
  • Assigning idempotent transaction identifiers across asynchronous message queues and event brokers
  • Preventing enumeration attacks where malicious users scrape records by incrementing numeric IDs
  • Deciding whether to use random UUID v4 or time-ordered UUID v7 for high-write database tables

The structural anatomy of a Version 4 UUID


The 128 bits of a UUID are standardly rendered in canonical 36-character hexadecimal notation formatted in five hyphen-separated blocks: 8-4-4-4-12 (32 hexadecimal characters plus 4 hyphens).

The Version and Variant bits: In the third segment (4xxx), the first hex character is permanently set to 4 (binary 0100), denoting UUID Version 4. In the fourth segment (yxxx), the first two most significant bits are fixed to 10, meaning the character y is always constrained to 8, 9, a, or b (in hexadecimal).

This leaves exactly 122 bits of pure entropy: 48 bits in the first two groups, 12 bits in the third group, and 62 bits across the fourth and fifth groups. Every valid UUID v4 will always display a 4 at position 15 and an 8, 9, a, or b at position 20.

Anatomy of the 5 segments in a canonical UUID v4 string
SegmentLengthBitsFixed / Variable Rules
Time-low (e.g. f47ac10b)8 hex chars32 bits100% random in Version 4
Time-mid (e.g. 58cc)4 hex chars16 bits100% random in Version 4
Version (e.g. 4372)4 hex chars16 bitsFirst character fixed to 4; 12 random bits
Variant (e.g. a567)4 hex chars16 bitsFirst char fixed to 8, 9, a, or b; 14 random bits
Node (e.g. 0e02b2c3d479)12 hex chars48 bits100% random in Version 4

The birthday paradox and collision math


Exactly how many UUIDs are there? 2^122 equals approximately 5.316 × 10^36 possible unique identifiers. This number is astronomical—exceeding the estimated number of grains of sand on planet Earth.

The Birthday Problem reality: Humans intuitively underestimate collision probability because we think about the chance of a new UUID colliding with one specific existing ID. But collisions occur when any two IDs in an entire collection match. The probability of collision p among n randomly generated items is approximated by the formula p ≈ n^2 / (2 × 2^122).

To have a 50% probability of finding even a single duplicate UUID v4, a system would have to generate approximately 2.71 × 10^18 UUIDs. If an application generated 1 billion UUIDs every second continuously, it would take roughly 85 years to reach that 50% threshold. For all practical commercial applications, treating properly generated UUID v4 values as globally unique is mathematically sound.

UUID v4 collision probabilities at different dataset scales
Total UUIDs Generated (n)Collision Probability (p)Real-World Scale Equivalent
1,000,000 (1 Million)≈ 9.4 × 10^-26A growing SaaS customer database
1,000,000,000 (1 Billion)≈ 9.4 × 10^-20High-volume telemetry events per month
100,000,000,000,000 (100 Trillion)≈ 9.4 × 10^-10Global IoT messaging over years
2,710,000,000,000,000,000 (2.71 Quintillion)50% (0.50)1 billion UUIDs/sec generated for 85 years

Cryptographic RNG versus Math.random()


Where the randomness comes from is the single most critical factor in UUID generation. Secure UUID generation requires a Cryptographically Secure Pseudo-Random Number Generator (CSPRNG), such as crypto.getRandomValues() in the Web Crypto API or crypto.randomUUID() in Node.js and modern browsers.

The disaster of Math.random(): Many legacy libraries or naive code snippets construct UUIDs using JavaScript Math.random(). Math.random() uses non-cryptographic PRNGs (such as xorshift128+) with limited internal state. Its pseudo-random sequence can be reverse-engineered after observing a small number of outputs, making generated identifiers completely guessable.

If you use guessable UUIDs for password reset tokens, document sharing URLs, or session identifiers, attackers can predict subsequent tokens and compromise user accounts. Always ensure generation relies on OS-level entropy pools (/dev/urandom or Windows CryptGenRandom).

// Cryptographically secure, native in all modern browsers and Node.js
const id = crypto.randomUUID();
console.log(id); // "f47ac10b-58cc-4372-a567-0e02b2c3d479"
Standard Web Crypto API generation (Native CSPRNG)

The B-tree fragmentation penalty in database primary keys


Why random UUID v4 degrades database write performance: Relational databases like PostgreSQL, MySQL (InnoDB), and SQLite store primary keys in clustered B-tree indexes. B-trees thrive on sequential, monotonically increasing values, which append neatly to the rightmost leaf page.

Random insertions cause frequent page splits: Because UUID v4 values are uniformly distributed, every new row is inserted into a random page in the B-tree. When an index leaf page fills up, the database engine must split it into two half-empty pages, doubling disk write I/O, fragmenting the memory buffer pool, and causing severe write latency spikes as tables grow past millions of rows.

The rise of UUID v7: For database primary keys where high-frequency inserts are required, modern standards (RFC 9562) specify UUID Version 7. UUIDv7 places a 48-bit millisecond timestamp in the leading bits, making keys naturally sortable while retaining 74 bits of random entropy. Use UUID v4 for external APIs and tokens, and UUID v7 for internal clustered indexes.

Hyphens, casing, and binary storage formats


Text vs binary footprint: Storing UUIDs as canonical 36-character strings (varchar(36)) consumes 36 bytes per row (or 144 bytes if indexed across multiple tables with UTF-8 collation). Storing them in their native 16-byte binary representation (uuid in PostgreSQL, binary(16) in MySQL) cuts storage and index footprint by more than 55%.

Case sensitivity and hyphens: RFC 4122 specifies that UUID hexadecimal digits should be output in lowercase. Comparing UUID strings with case-sensitive string equality checks can fail if another system emits uppercase letters. Strip hyphens or lowercase strings before storing or comparing if not using a native UUID data type.

Many database management tools format UUIDs differently: Microsoft SQL Server displays them in uppercase with curly braces ({GUID}), while Unix and Python environments prefer lowercase hyphenated strings. Always canonicalize incoming strings at your application boundary.

Common problems


Each of these is something that actually happens, with the cause rather than a generic suggestion to check your input.

Two identical UUIDs were generated on different machines

Cause
The application used a non-cryptographic random generator seeded with identical timestamps or empty seed states in containerized environments.
Fix
Use cryptographically secure RNGs (such as crypto.randomUUID() or /dev/urandom) that pull system entropy directly from the OS kernel.

Database write queries slowed down as the table grew past 10 million rows

Cause
Random UUID v4 primary keys caused rampant B-tree page splits and cache churn across the clustered index.
Fix
Migrate primary keys to time-ordered UUID v7, or keep an internal sequential bigint primary key while exposing UUID v4 as a secondary unique identifier.

UUID comparison failed between two systems despite having identical characters

Cause
One system formatted the UUID with uppercase letters or curly braces ({guid}), while the other used canonical lowercase with hyphens.
Fix
Normalize UUID strings to lowercase and strip optional surrounding braces before performing string equality comparisons.

UUID lookup in SQL failed with invalid input syntax

Cause
The string contained invalid hex characters or misplaced hyphens (must match 8-4-4-4-12 format).
Fix
Validate the UUID against the RFC 4122 regex pattern ^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ before querying.

Questions


Is it possible for two UUID v4 values to collide?
Theoretically yes, but practically impossible. With 122 bits of cryptographic entropy, the probability of generating a duplicate is so small that a system would need to generate billions of IDs per second for decades before hitting an even chance of a single collision.
Should I use UUID v4 or UUID v7 for database primary keys?
Use UUID v7 for database primary keys because its millisecond timestamp prefix prevents B-tree index fragmentation. Use UUID v4 for security tokens, public API slugs, and session IDs where timestamp leakage is undesirable.
Why does every UUID v4 have a 4 in the third segment?
RFC 4122 reserves the first 4 bits of the third segment to identify the UUID version. For Version 4, this hex digit is always fixed to 4.
Are UUIDs generated in the browser secure for production use?
Yes, provided they use the Web Crypto API (crypto.randomUUID() or crypto.getRandomValues()), which sources cryptographically secure random entropy directly from the operating system.