UUID v4: a complete guide to universally unique identifiers
UUID (Universally Unique Identifier) is an identifier standard that lets you generate unique values without a central coordinator. The specification is described in RFC 4122 and is widely used in distributed systems, databases, APIs, and file systems. This article covers the UUID format, its versions, practical uses, and how to generate UUIDs in popular languages and databases.
What is a UUID?
A UUID (also known as a GUID — Globally Unique Identifier in the Microsoft ecosystem) is a 128-bit identifier represented as 32 hexadecimal characters separated by hyphens.
Format: 8-4-4-4-12
The standard UUID format is xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, where each group corresponds to a specific number of characters:
- 8 characters — the first 32 bits (time_low)
- 4 characters — the next 16 bits (time_mid)
- 4 characters — the version and 12 bits (time_hi_and_version)
- 4 characters — the variant and 12 bits (clock_seq_and_reserved, clock_seq_low)
- 12 characters — 48 bits (node)
Example: 550e8400-e29b-41d4-a716-446655440000
The string representation is 36 characters long (32 hex characters + 4 hyphens). In binary form, a UUID occupies exactly 16 bytes, which is important to account for when designing a database schema.
UUID versions
RFC 4122 defines several UUID versions that differ in how they are generated.
UUID v1 — time-based
UUID v1 uses a network card’s MAC address and a timestamp (100-nanosecond intervals since 15 October 1582). The identifiers are lexicographically sortable by creation time, which is useful for indexing. The drawback is that exposing the MAC address creates privacy risks, so v1 is rarely used in public APIs.
UUID v4 — random
UUID v4 is the most common version in web development. 122 of its 128 bits are generated by a cryptographically secure random number generator (CSPRNG); 6 bits are reserved for the version (0100 at position 13) and the variant (10 at position 17). It is ideal for most scenarios where sorting by time is not required. It is built into all modern languages and databases.
UUID v5 — namespace- and name-based
UUID v5 is generated deterministically using a SHA-1 hash of a namespace UUID and a name. The same inputs always produce the same UUID. It is useful for creating identifiers from names (for example, domain names or resource names). Standard namespaces include DNS (6ba7b810-9dad-11d1-80b4-00c04fd430c8) and URL (6ba7b811-9dad-11d1-80b4-00c04fd430c8). UUID v4 and v5 are the most common versions in web development.
UUID v7 — time + randomness (new standard)
UUID v7 is a relatively new variant (RFC draft) that combines a timestamp with random bits. Its identifiers are sortable by time, like v1, but do not expose the MAC address. It is recommended for new projects where sorting is important.
UUID v4 in detail
In UUID v4, 122 bits are random. The version (4) is encoded at the position of the 13th character, and the variant at the 17th.
Collision probability
With 122 random bits, the value space is 2^122 ≈ 5,3×10^36. According to the collision probability formula (the birthday paradox), generating approximately 2,7×10^18 UUIDs would be required to reach a 50% collision probability. For comparison, generating a billion UUIDs per second would take 85 years to reach this threshold. In practice, UUID v4 collisions are considered impossible at reasonable generation volumes — ensuring that the chosen library uses a cryptographically secure PRNG is more important.
UUID vs. auto-increment
The choice between UUIDs and auto-incrementing integers depends on the system architecture.
Advantages of UUIDs
- Distributed systems — generate IDs without contacting the database; multiple nodes can create records independently
- Database merging — ID conflicts are avoided when merging data from different sources
- Security — the next ID is harder to guess, reducing the risk of enumeration in APIs
- Client-side generation — an ID can be created on the client before sending it to the server (offline-first)
Disadvantages of UUIDs
- Size — 16 bytes versus 4–8 bytes for an integer
- Index performance — random values reduce B-tree index efficiency because of fragmentation
- Readability — long strings are inconvenient for debugging and logs
- Sorting — UUID v4 identifiers are not sortable by creation time
When to choose UUIDs
Use UUIDs for a microservices architecture, replication across data centers, multitenancy with data isolation, public APIs with sensitive IDs, or offline-first applications with synchronization. Use auto-increment if you have a monolithic architecture, a very high write load (tens of thousands of inserts per second), index performance is critical, or compact storage is required. A hybrid approach is to use UUIDs for external APIs and inter-service communication, and internal integers for relationships between tables within a single database — this balances convenience and performance.
Generating UUIDs
JavaScript
In modern browsers and Node.js 19, crypto.randomUUID() is available:
// Браузер и Node.js 19+
const id = crypto.randomUUID();
console.log(id); // "550e8400-e29b-41d4-a716-446655440000"For Node.js versions before 19, use the uuid package:
const { v4: uuidv4 } = require('uuid');
const id = uuidv4();Python
Standard library uuid:
import uuid
id = uuid.uuid4()
print(id) # 550e8400-e29b-41d4-a716-446655440000
print(str(id)) # строковое представлениеPostgreSQL
Built-in function gen_random_uuid() (requires the pgcrypto extension in older versions; built in starting with PostgreSQL 13):
SELECT gen_random_uuid();
-- a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11MySQL
SELECT UUID();
-- или MySQL 8.0.13+:
SELECT UUID_TO_BIN(UUID());Other languages
In Go: the github.com/google/uuid package, uuid.New() function. In Java: java.util.UUID.randomUUID(). In C#: Guid.NewGuid(). In Ruby: require 'securerandom' and SecureRandom.uuid. All these implementations comply with RFC 4122 and generate cryptographically secure random values for v4.
UUIDs in databases
Storage: BINARY(16) vs. CHAR(36)
It is recommended to store UUIDs in binary format:
| Type | Size | Indexing |
|---|---|---|
| CHAR(36) | 36 bytes | Slower |
| BINARY(16) | 16 bytes | More efficient |
In PostgreSQL, use the uuid type — it is optimized, occupies 16 bytes, and supports format validation at the type level. MySQL has no native UUID type before version 8.0, so BINARY(16) with the UUID_TO_BIN and BIN_TO_UUID functions is the recommended approach:
CREATE TABLE users (
id BINARY(16) PRIMARY KEY,
email VARCHAR(255)
);
-- Вставка
INSERT INTO users (id, email) VALUES (UUID_TO_BIN(UUID()), 'user@example.com');Indexing
Random UUIDs create “hot spots” in B-tree indexes: new pages are inserted in random locations, increasing the number of split operations and fragmentation. Optimization options:
- UUID v7 — time-based sorting reduces fragmentation, and new records are added to the end of the index
- Byte reordering — store time_low at the beginning of the UUID (if using v1) to improve locality during inserts
- Partitioning — partition by UUID prefix to distribute load across partitions
- Composite keys — (tenant_id, uuid) for multitenant systems, improving isolation and caching
When choosing a primary key, keep in mind that in PostgreSQL and MySQL, clustered indexes (primary keys) determine the physical order of data on disk. A random UUID results in rows being distributed randomly, which can reduce the efficiency of sequential reads.
UUIDs in APIs and URLs
UUIDs work well as public identifiers in REST APIs:
GET /api/users/550e8400-e29b-41d4-a716-446655440000Advantages: neighboring IDs cannot be enumerated (unlike /users/1, /users/2), they remain stable when systems are merged, and they do not reveal the number of records. The drawback is long URLs (36 characters). For short links, consider base62 encoding (22 characters) or separate short identifiers (nanoid, ulid). UUIDs are usually sent in headers and cookies without hyphens to save space.
Common usage patterns
UUID as a primary key
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id),
total DECIMAL(10,2)
);UUID as an API token
UUID v4 is suitable for access tokens: 122 bits of entropy provide sufficient resistance to enumeration. However, for high-load systems and tokens with a long lifetime, cryptographically secure random strings are preferable (for example, 256 bits encoded in base64url) — they provide more entropy at a comparable length. UUIDs are convenient for one-time verification codes, password reset links, and session identifiers.
UUIDs in file names
Using UUIDs in file names prevents collisions during uploads:
uploads/550e8400-e29b-41d4-a716-446655440000.pdfPerformance
Key considerations when working with UUIDs in production:
- Index size — UUIDs increase index size by 2–4 times compared with BIGINT (8 bytes). For a table with 10 million records, the difference can amount to hundreds of megabytes
- Comparison — binary comparison of 16 bytes is faster than string comparison of 36 characters. Always use the native UUID type or BINARY(16), not VARCHAR/CHAR
- Fragmentation — with a high insertion rate, indexes on UUID v4 fragment more than those on sequential values. Periodic REINDEX (PostgreSQL) or OPTIMIZE TABLE (MySQL) may be needed more often
- Caching — random UUIDs make less effective use of the database page cache because access is distributed across the entire index
- Benchmarks — in insertion tests, UUIDs typically result in a 10–30% performance drop compared with auto-increment; this is acceptable for most applications (up to thousands of inserts per second). Reads by primary key are practically unaffected
Common mistakes when working with UUIDs
Avoid storing UUIDs as strings in an unsuitable format: some ORMs use VARCHAR(36) by default, which is suboptimal. Check letter case when comparing values — the standard requires lowercase, but not all libraries guarantee it. Do not use UUID v1 in public systems because it leaks the MAC address. When migrating from integers to UUIDs, plan to update foreign keys and indexes — the operation can take a considerable amount of time on large tables.
Conclusion
UUID v4 is a reliable choice for distributed systems, APIs, and scenarios where independent identifier generation is important. Understanding the 8-4-4-4-12 format, the differences between versions, and the performance trade-offs helps you make an informed choice between UUIDs and auto-increment. For new projects that require sorting by creation time, consider UUID v7.
To quickly generate a UUID in your browser, use the online UUID generator.