1. What is a UUID?
UUID (Universally Unique Identifier) is a 128-bit identifier designed to be unique across all computers, all time, without requiring a central authority. Also known as GUID (Globally Unique Identifier) in Microsoft ecosystems.
Example UUIDs:
550e8400-e29b-41d4-a716-446655440000 (v1)
f47ac10b-58cc-4372-a567-0e02b2c3d479 (v4)
01932c08-e844-7d3f-8d5e-9c1b5d62a3e8 (v7)
Key Characteristics:
- 128 bits: Represented as 32 hexadecimal characters
- Globally unique: No central coordination needed
- Standardized: Defined in RFC 4122 (and RFC 9562 for v7)
- Platform-independent: Works across all systems
Common Use Cases:
- Database primary keys
- Distributed system identifiers
- Session tokens and correlation IDs
- File and resource naming
- API request tracking
2. UUID Format and Structure
UUIDs follow a standard format: 32 hexadecimal digits displayed as 8-4-4-4-12 groups separated by hyphens.
UUID Structure
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
Version and Variant Indicators:
Look at the 13th character (M) to identify the version:
xxxxxxxx-xxxx-1xxx-xxxx-xxxxxxxxxxxx→ Version 1xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx→ Version 4xxxxxxxx-xxxx-7xxx-xxxx-xxxxxxxxxxxx→ Version 7
The variant (N) indicates the UUID variant. For standard UUIDs, it starts with 8,
9, a, or b.
3. UUID Versions Explained
| Version | Based On | Sortable | Recommended |
|---|---|---|---|
| v1 | Timestamp + MAC address | Partially | ⚠️ Privacy concern |
| v2 | DCE Security | No | ❌ Rarely used |
| v3 | MD5 hash of namespace + name | No | ⚠️ Use v5 instead |
| v4 | Random | No | ✅ Good default |
| v5 | SHA-1 hash of namespace + name | No | ✅ Deterministic |
| v6 | Reordered v1 (time-sortable) | Yes | ✅ When v1 sorting needed |
| v7 | Unix timestamp + random | Yes | ✅ Modern choice |
Version 1: Timestamp + MAC Address
Combines 60-bit timestamp (100-nanosecond intervals since Oct 15, 1582) with MAC address. Privacy concern: Exposes when and where the UUID was created.
// v1 structure reveals information
f81d4fae-7dec-11d0-a765-00a0c91e6bf6
↑ ↑
timestamp MAC address
Version 4: Random
122 bits of randomness (6 bits reserved for version/variant). Most commonly used. Simple, no information leakage, but not sortable.
// v4 - pure randomness
f47ac10b-58cc-4372-a567-0e02b2c3d479
↑ ↑
version variant (4 and 8/9/a/b)
Version 5: Name-Based (SHA-1)
Deterministic: same namespace + name always produces the same UUID. Useful for generating consistent IDs from known data.
// Same input = same UUID
uuid_v5(DNS_NAMESPACE, "example.com") → always same result
uuid_v5(URL_NAMESPACE, "https://example.com/page") → always same result
Version 7: Time-Ordered Random (Recommended)
48-bit Unix timestamp (milliseconds) + 74 bits of randomness. Best of both worlds: sortable by creation time while being mostly random. Ideal for database primary keys.
// v7 - sortable by time, generated at the same millisecond
01932c08-e844-7d3f-8d5e-9c1b5d62a3e8 (earlier)
01932c08-e845-7a2b-9f3c-8d4e7a6b5c9d (later within same ms)
↑
timestamp (ms since Unix epoch)
4. Which UUID Version Should I Use?
🏆 UUID v7 - Modern Applications
Best choice for new projects. Time-sortable for database efficiency, random enough for security, and chronologically meaningful. Use this if your language/library supports it.
✅ UUID v4 - Simple & Secure
Great default choice. Maximum randomness, no information leakage, widely supported. Choose v4 if you don't need sorting or if v7 isn't available.
🔗 UUID v5 - Deterministic IDs
When you need the same ID for the same input (idempotency). Great for creating IDs from URLs, emails, or other unique strings.
⚠️ UUID v1 - Legacy Only
Avoid for new projects. Exposes MAC address (privacy) and creation time. Only use for compatibility with existing v1 systems.
Decision Flowchart:
Need deterministic IDs? → UUID v5
Need time-sortable IDs? → UUID v7
Need maximum randomness? → UUID v4
Legacy system compatibility? → Match existing version
Default for new projects → UUID v7 (or v4 if unsupported)
5. Collision Probability
The question everyone asks: Can two UUIDs ever be the same?
TL;DR: Practically Impossible
UUID v4 has 122 random bits = 5.3 × 1036 possible values. You'd need to generate 2.71 quintillion (2.71 × 1018) UUIDs for a 50% collision chance.
The Math:
Possible UUIDs = 2122 ≈ 5,316,911,983,139,663,491,615,228,241,121,378,304
That's over 5 undecillion unique values
Perspective:
- Generating 1 billion UUIDs per second for 86 years gives ~50% collision chance
- Generating 1 trillion UUIDs = 0.00000000006% collision chance
- You're more likely to be struck by lightning while winning the lottery
⚠️ The Real Risk
Collisions almost always come from bad random number generators, not mathematical probability. Always use cryptographically secure randomness (CSPRNG) for UUID generation.
6. UUIDs in Databases
Pros of UUID Primary Keys:
- ✅ Generate IDs client-side (no round-trip to database)
- ✅ Easy database merging and sharding
- ✅ No enumeration attacks (can't guess next ID)
- ✅ Hide row count from API consumers
Cons of UUID Primary Keys:
- ❌ Larger than integers (16 bytes vs 4-8 bytes)
- ❌ Random UUIDs cause index fragmentation (B-tree)
- ❌ Less human-readable for debugging
- ❌ Slightly slower joins and lookups
Database-Specific Storage:
| Database | Native Type | Storage |
|---|---|---|
| PostgreSQL | UUID |
16 bytes (native) |
| MySQL 8.0+ | BINARY(16) |
16 bytes |
| SQL Server | UNIQUEIDENTIFIER |
16 bytes (native) |
| SQLite | TEXT or BLOB |
36 or 16 bytes |
| MongoDB | BinData subtype 4 |
16 bytes |
PostgreSQL Example:
-- Enable UUID extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Create table with UUID primary key
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
-- or for v7: gen_random_uuid() in PG 13+
email VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
-- Insert with auto-generated UUID
INSERT INTO users (email) VALUES ('user@example.com');
-- Or specify UUID
INSERT INTO users (id, email)
VALUES ('550e8400-e29b-41d4-a716-446655440000', 'user2@example.com');
💡 Pro Tip: Use UUID v7 for Database Keys
UUID v7 is time-ordered, which means new records are always inserted at the end of the B-tree index. This eliminates the fragmentation problem of random UUIDs while keeping all the benefits.
7. Implementation Examples
JavaScript / Node.js
// Node.js (built-in since v14.17.0)
import { randomUUID } from 'crypto';
const uuid = randomUUID(); // v4
// Browser (modern browsers)
const uuid = crypto.randomUUID(); // v4
// Using uuid library (supports all versions)
import { v4 as uuidv4, v5 as uuidv5, v7 as uuidv7 } from 'uuid';
const id = uuidv4(); // Random
const id7 = uuidv7(); // Time-ordered (v7)
// v5 - deterministic from namespace + name
const DNS_NAMESPACE = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
const id5 = uuidv5('example.com', DNS_NAMESPACE);
// Validate UUID
function isValidUUID(str) {
const regex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-7][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
return regex.test(str);
}
Python
import uuid
# UUID v4 - Random
id_v4 = uuid.uuid4()
print(id_v4) # e.g., f47ac10b-58cc-4372-a567-0e02b2c3d479
# UUID v1 - Timestamp + MAC (avoid for privacy)
id_v1 = uuid.uuid1()
# UUID v5 - Name-based (SHA-1)
id_v5 = uuid.uuid5(uuid.NAMESPACE_DNS, 'example.com')
# Always produces: cfbff0d1-9375-5685-968c-48ce8b15ae17
# UUID v7 (Python 3.12+)
# id_v7 = uuid.uuid7()
# Convert to/from string
str_uuid = str(id_v4)
parsed = uuid.UUID(str_uuid)
# Get bytes (for database storage)
uuid_bytes = id_v4.bytes # 16 bytes
Java
import java.util.UUID;
public class UuidExample {
public static void main(String[] args) {
// UUID v4 - Random
UUID uuid = UUID.randomUUID();
System.out.println(uuid);
// UUID v3/v5 - Name-based
UUID namespace = UUID.fromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8");
UUID nameUuid = UUID.nameUUIDFromBytes("example.com".getBytes());
// Parse from string
UUID parsed = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
// Get version
int version = uuid.version(); // 4
// Convert to bytes (for database)
long msb = uuid.getMostSignificantBits();
long lsb = uuid.getLeastSignificantBits();
}
}
Go
package main
import (
"fmt"
"github.com/google/uuid"
)
func main() {
// UUID v4
id := uuid.New()
fmt.Println(id) // e.g., 6ba7b810-9dad-11d1-80b4-00c04fd430c8
// UUID v7 (time-ordered)
id7, _ := uuid.NewV7()
fmt.Println(id7)
// Parse from string
parsed, _ := uuid.Parse("550e8400-e29b-41d4-a716-446655440000")
// Check version
fmt.Println(parsed.Version()) // 1, 4, 7, etc.
}
PHP
<?php
// Using ramsey/uuid library
use Ramsey\Uuid\Uuid;
// UUID v4 - Random
$uuid = Uuid::uuid4();
echo $uuid->toString(); // e.g., f47ac10b-58cc-4372-a567-0e02b2c3d479
// UUID v7 - Time-ordered
$uuid7 = Uuid::uuid7();
// UUID v5 - Name-based
$uuid5 = Uuid::uuid5(Uuid::NAMESPACE_DNS, 'example.com');
// Parse and validate
$parsed = Uuid::fromString('550e8400-e29b-41d4-a716-446655440000');
$isValid = Uuid::isValid($someString);
// Get bytes for database
$bytes = $uuid->getBytes(); // 16 bytes
?>
8. UUID Alternatives
| Format | Length | Sortable | Notes |
|---|---|---|---|
| UUID v4 | 36 chars | No | Standard, widely supported |
| UUID v7 | 36 chars | Yes | Time-sorted, new standard |
| ULID | 26 chars | Yes | Crockford Base32, URL-safe |
| nanoid | 21 chars | No | Compact, URL-safe |
| CUID2 | 24 chars | Partially | Secure, collision-resistant |
| Snowflake | 64-bit int | Yes | Twitter's format, needs coordination |
ULID Example:
// ULID format: 01ARZ3NDEKTSV4RRFFQ69G5FAV
// 48-bit timestamp + 80-bit random
// Lexicographically sortable, URL-safe
import { ulid } from 'ulid';
const id = ulid(); // 01ARZ3NDEKTSV4RRFFQ69G5FAV
nanoid Example:
// nanoid: V1StGXR8_Z5jdHi6B-myT
// Smaller, faster, URL-safe
import { nanoid } from 'nanoid';
const id = nanoid(); // V1StGXR8_Z5jdHi6B-myT
9. Best Practices
✅ Do: Use UUID v7 for new database primary keys
Time-ordered UUIDs prevent index fragmentation while maintaining all benefits of UUIDs.
✅ Do: Store as binary when possible
16 bytes binary vs 36 bytes string saves storage and improves index performance.
✅ Do: Use v5 for deterministic IDs
When you need idempotent ID generation from known data (URLs, emails, etc.).
❌ Don't: Use UUID v1 in new applications
Exposes MAC address and exact creation timestamp. Privacy and security concern.
❌ Don't: Rely on UUID ordering with v4
Random UUIDs have no guaranteed order. Use v7 if you need time-based sorting.
❌ Don't: Create your own UUID generation
Use well-tested libraries. DIY implementations often have weak randomness.
10. Frequently Asked Questions
What is the difference between UUID and GUID?
Are UUIDs truly unique?
Should I use UUIDs or auto-increment integers for primary keys?
Integers: Smaller storage, faster indexes, human-readable for debugging.
Recommendation: Use UUID v7 for most applications. Use integers only if storage/performance is critical and you don't need distributed ID generation.
Can I extract timestamp from a UUID?
v4: No, it's purely random.
For v7, extract the first 48 bits and interpret as Unix milliseconds. Many libraries provide helper functions for this.
Why does UUID v1 expose MAC address?
How do I validate a UUID string?
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-7][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iThis validates the format, version (1-7), and variant (8/9/a/b). Most UUID libraries also provide validation functions.
What is a nil UUID?
00000000-0000-0000-0000-000000000000. It's used
to represent the absence of a UUID or as a placeholder. Think of it like
null for UUIDs. There's also a max UUID (all f's) sometimes used for upper
bounds.
Can I use UUIDs in URLs?
/users/550e8400-e29b-41d4-a716-446655440000. For shorter URLs, consider
ULID or nanoid, or encode UUIDs in Base64 (but you lose readability).
How much storage does a UUID need?
String with hyphens: 36 bytes (32 hex + 4 hyphens)
String without hyphens: 32 bytes
For databases, always use binary storage when possible. A BIGINT is only 8 bytes by comparison.
Is UUID v7 ready for production use?
uuid (JS), Go's standard library, and many others. It combines the benefits
of time-sorting with random uniqueness. It's the recommended choice for new
applications.
Need to generate UUIDs?
Try our free UUID generator. Create v1, v4, v5, and v7 UUIDs instantly - all client-side.
Open UUID Generator