UUIDs Explained: Versions and Best Practices
8 min read

UUIDs Explained: Versions and Best Practices

Everything you need to know about UUIDs. Learn the differences between UUID versions and when to use each.

uuiddatabasetutorialidentifiers
Share:

UUIDs Explained: Versions, Use Cases, and Best Practices

Choose the right UUID version for your application.


What is a UUID?

A UUID (Universally Unique Identifier) is a 128-bit identifier designed to be unique across space and time without central coordination. Also known as GUID (Globally Unique Identifier) in Microsoft ecosystems.

550e8400-e29b-41d4-a716-446655440000

The format is 32 hexadecimal digits displayed in five groups separated by hyphens: 8-4-4-4-12.

Why UUIDs?

Traditional auto-incrementing IDs have problems:

  • Require coordination - A central database must assign IDs
  • Expose information - ID 1000 reveals ~1000 records exist
  • Merge conflicts - Combining databases causes ID collisions
  • Scaling limits - Single point of failure for ID generation

UUIDs solve these by generating IDs anywhere, anytime, with virtually zero collision risk.


UUID Versions

Not all UUIDs are created equal. There are five standard versions, each with different properties.

UUID v1 (Timestamp + MAC Address)

Structure: 60-bit timestamp + 14-bit sequence + 48-bit MAC address

|      timestamp      |ver| seq |  MAC address  |
f47ac10b-58cc-11e8-a5c3-0242ac130002

Characteristics:

  • Time-ordered (somewhat sortable)
  • Reveals creation time
  • Reveals machine MAC address
  • Low collision risk within same machine

When to use: Rarely. The MAC address leak is a privacy/security concern. Mostly seen in legacy systems.

When to avoid: Any system where revealing creation time or machine identity is problematic.


UUID v4 (Random)

Structure: 122 random bits + 6 version/variant bits

|            random bits            |
f47ac10b-58cc-4d7a-9d7a-0242ac130002
                ^
                version 4 marker

Characteristics:

  • Completely random
  • No embedded information
  • Not sortable
  • 2^122 possible values (~5.3 × 10^36)

Collision probability: If you generate 1 billion UUIDs per second, it would take about 100 years to have a 50% chance of a single collision.

When to use:

  • Most applications
  • When you don't need time-ordering
  • Security-sensitive contexts (no information leakage)

When to avoid:

  • Database primary keys in high-write scenarios (fragmentation)
  • When you need sortability

UUID v7 (Time-Ordered Random) - NEW STANDARD

Structure: 48-bit timestamp (ms) + 74 random bits + 6 version/variant bits

|   timestamp (ms)   |ver|  random   |
0186d5a5-9e44-7xxx-xxxx-xxxxxxxxxxxx

Characteristics:

  • Time-ordered (sortable!)
  • Random component prevents conflicts
  • Millisecond precision timestamp
  • Better database performance than v4
  • No MAC address exposure

When to use:

  • Database primary keys (sortable, index-friendly)
  • Distributed systems (time-ordered, no coordination)
  • New projects (modern, best of both worlds)
  • Event sourcing (chronological ordering)

When to avoid:

  • If you need nanosecond precision (use v1 or custom)
  • Legacy systems expecting v4 format (rare issue)

Why v7 is better for databases:

With v4's random distribution, new records are inserted throughout the B-tree index, causing page splits and fragmentation. With v7's time-ordering, new records are appended to the end, similar to auto-increment behavior, but without coordination requirements.

Generate: UUID Generator (supports both v4 and v7)


UUID v3 and v5 (Name-Based)

Structure: Hash of namespace + name

v3: MD5 hash (128 bits, 122 usable)
v5: SHA-1 hash (160 bits truncated, 122 usable)

Characteristics:

  • Deterministic - same input produces same UUID
  • Requires namespace UUID + string name
  • v5 (SHA-1) preferred over v3 (MD5)

Example:

// Same input always produces same UUID
uuidv5('hello', NAMESPACE_DNS); // Always: 9342d47a-1bab-5709-9ee0-c85352fd3e4a
uuidv5('hello', NAMESPACE_DNS); // Always: 9342d47a-1bab-5709-9ee0-c85352fd3e4a

When to use:

  • Generating consistent IDs from names
  • URL-based identifiers
  • When you need idempotent ID generation

Standard namespaces:

  • 6ba7b810-9dad-11d1-80b4-00c04fd430c8 - DNS
  • 6ba7b811-9dad-11d1-80b4-00c04fd430c8 - URL
  • 6ba7b812-9dad-11d1-80b4-00c04fd430c8 - ISO OID
  • 6ba7b814-9dad-11d1-80b4-00c04fd430c8 - X.500 DN

UUID Version Comparison

VersionMethodSortableInfo LeakageDB Performance
v1Time + MACPartialHigh (time, MAC)Good
v3MD5 hashNoNonePoor
v4RandomNoNonePoor
v5SHA-1 hashNoNonePoor
v7Time + RandomYesLow (time only)Excellent

UUID vs Other ID Formats

Auto-Increment

1, 2, 3, 4, 5...
AspectAuto-IncrementUUID
Size4-8 bytes16 bytes
CoordinationRequired (DB)None
SortableYesv7 only
Information leakCount exposedNone (v4) or time (v7)
Merge-safeNoYes

Choose auto-increment when: Single database, low scale, foreign key optimization matters.

Choose UUID when: Distributed systems, multiple databases, security/privacy concerns.


ULID (Universally Unique Lexicographically Sortable Identifier)

01ARZ3NDEKTSV4RRFFQ69G5FAV
AspectULIDUUID v7
Length26 chars36 chars
EncodingBase32Hexadecimal
SortableYesYes
Timestamp48-bit ms48-bit ms
Randomness80 bits74 bits

Choose ULID when: String length matters, case-insensitive contexts.

Choose UUID v7 when: Broader compatibility, standard format expected.


Snowflake IDs (Twitter)

1234567890123456789 (64-bit integer)
AspectSnowflakeUUID
Size8 bytes16 bytes
StructureTime + Machine + SeqVaries by version
SortableYesv7 only
CoordinationMachine ID neededNone

Choose Snowflake when: Integer IDs required, high throughput, controlled infrastructure.

Choose UUID when: No machine ID coordination, broader compatibility.


NanoID

V1StGXR8_Z5jdHi6B-myT

Custom-length random IDs with configurable alphabet.

AspectNanoIDUUID
LengthConfigurableFixed 36
AlphabetConfigurableHex
SortableNov7 only
StandardNoYes (RFC 4122)

Choose NanoID when: URL-friendly IDs, custom length requirements.

Choose UUID when: Interoperability, standard compliance.


Best Practices

For Database Primary Keys

  1. Use UUID v7 for new projects - sortable, distributed-friendly, index-efficient
  2. Store as binary (BINARY(16) not VARCHAR(36)) - saves space, faster comparisons
  3. Consider column order - put UUID columns after frequently-queried columns in composite indexes
-- Good: Binary storage
CREATE TABLE users (
  id BINARY(16) PRIMARY KEY,
  email VARCHAR(255),
  -- Convert for display: BIN_TO_UUID(id)
);

-- Avoid: String storage (wastes 20 bytes per row)
CREATE TABLE users (
  id VARCHAR(36) PRIMARY KEY,
  ...
);

For APIs

  1. Use lowercase with hyphens: 550e8400-e29b-41d4-a716-446655440000
  2. Validate format on input - reject malformed UUIDs
  3. Consider case-insensitivity - treat 550E8400 same as 550e8400

For Security

  1. Never use v1 if MAC address exposure is a concern
  2. v4 reveals nothing - safe for user-facing IDs
  3. v7 reveals creation time - may be acceptable depending on context
  4. Don't trust client-generated UUIDs - validate and potentially regenerate server-side

For Performance

  1. Batch generation - generate multiple UUIDs at once if needed
  2. Use native implementations - crypto libraries, not manual string manipulation
  3. Avoid string comparisons - compare as bytes when possible

Common Mistakes

1. Using UUID for Everything

UUIDs add overhead. For internal lookup tables with fewer than 1000 rows, auto-increment is simpler and faster.

2. Storing as Strings

VARCHAR(36) uses 37+ bytes. BINARY(16) uses exactly 16 bytes. That's 2.3x the storage for no benefit.

3. Assuming Chronological Order with v4

UUID v4 is random. Don't use it for "latest first" queries. Use v7 or add a timestamp column.

4. Generating in Browser without Crypto

// Bad: Math.random() is predictable
function badUUID() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
    const r = Math.random() * 16 | 0;
    return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
  });
}

// Good: Use crypto API
crypto.randomUUID(); // Native, secure

5. Ignoring Index Fragmentation

Random v4 UUIDs cause B-tree page splits. For high-write tables, this degrades performance over time. Use v7 or ULID instead.


UUID in Different Languages

JavaScript

// Native (modern browsers/Node 19+)
crypto.randomUUID(); // v4

// uuid package
import { v4 as uuidv4, v7 as uuidv7 } from 'uuid';
uuidv4(); // Random
uuidv7(); // Time-ordered

Python

import uuid

uuid.uuid4()  # Random
uuid.uuid1()  # Time + MAC

# v7 (Python 3.13+ or uuid7 package)
import uuid7
uuid7.uuid7()

Go

import "github.com/google/uuid"

uuid.New()        // v4
uuid.NewV7()      // v7

Java

import java.util.UUID;

UUID.randomUUID();  // v4

// v7 (Java 21+ or library)

PostgreSQL

-- Extension required
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

-- Generate
SELECT uuid_generate_v4();  -- Random
SELECT gen_random_uuid();   -- v4 (built-in since v13)

Conclusion

For most new projects, use UUID v7. It combines the best properties: globally unique without coordination, time-sortable for database efficiency, and no MAC address leakage.

Use v4 when you need maximum randomness and don't care about sorting. Use name-based (v5) for deterministic generation from strings.

Avoid v1 in new projects (MAC exposure) and v3 (MD5 is deprecated).


Generate UUIDs Now

Need UUIDs for your project? Use our UUID Generator to:

  • Generate v4 (random) or v7 (time-ordered) UUIDs
  • Generate in bulk (up to 1000 at once)
  • Validate existing UUIDs
  • Copy with one click

Last updated: January 2026

Related Tools

Related Articles