23. Unique ID Generation at Scale | System Design - Study Chapter | QuizMaker

How to generate billions of unique, sortable, distributed-friendly identifiers without coordination.

Read
12m
Type
Chapter
Access
Free

Course

System Design: The Complete Guide

Topic

5. Advanced Concepts & Patterns

[!NOTE] In a monolith with a single database, auto-incrementing IDs (id SERIAL) work fine. In a distributed system with multiple database shards, multiple services, and messages flowing through queues, you need IDs that are globally unique without coordination, roughly sortable by time, and fast to generate.

Why Auto-Increment Fails in Distributed Systems

UUID v4 (Random)

UUID v4 generates 128-bit random identifiers: 550e8400-e29b-41d4-a716-446655440000

UUID v7 (Timestamp-Ordered)

UUID v7 (RFC 9562, 2024) embeds a Unix timestamp in the first 48 bits, followed by random bits:

  Timestamp (48 bits)  |  Random (80 bits)
  ─────────────────────┼──────────────────
  018f5e3c-7a00       |  7xxx-xxxx-......

Twitter Snowflake

Twitter created the Snowflake ID generator in 2010 to produce unique, time-sorted, 64-bit integers at massive scale. A Snowflake ID is a 64-bit integer packed as:

  0 | Timestamp (41 bits)     | Datacenter (5 bits) | Machine (5 bits) | Sequence (12 bits)
  ─┼─────────────────────────┼────────────────────┼──────────────────┼────────────────────
    | Milliseconds since epoch| 32 datacenters     | 32 machines/DC   | 4096 IDs/ms/machine

Clock Skew: The Achilles'' Heel

Snowflake relies on system clocks. If a machine''s clock drifts backward (e.g., after NTP correction), it could generate duplicate IDs. Solutions:

Comparison Table

MethodSizeSortableCoordinationDB Index PerformanceCollision Risk
DB Auto-Increment32/64 bitYesCentralized DBExcellentNone (single source)
UUID v4128 bitNoNonePoor (random scattering)Near zero
UUID v7128 bitYesNoneGood (time-ordered)Near zero
Snowflake64 bitYesMachine ID assignmentExcellentNear zero

Real-World Usage

Snowflake ID: Implementation Deep Dive

Snowflake ID Structure (64 bits):

| 1 bit  |  41 bits         | 10 bits    | 12 bits      |
| unused |  timestamp (ms)  | machine ID | sequence num |

timestamp: milliseconds since custom epoch (e.g., 2020-01-01)
  → 2^41 ms = ~69 years before overflow
machine_id: unique per server (1024 possible machines)
sequence: per-millisecond counter (4096 IDs per ms per machine)

Max throughput per machine: 4,096,000 IDs/second
Max throughput (1024 machines): ~4 billion IDs/second
class SnowflakeGenerator:
    EPOCH = 1577836800000  # 2020-01-01 00:00:00 UTC

    def __init__(self, machine_id):
        self.machine_id = machine_id  # 0-1023
        self.sequence = 0
        self.last_timestamp = -1

    def generate(self):
        timestamp = current_time_ms() - self.EPOCH

        if timestamp == self.last_timestamp:
            self.sequence = (self.sequence + 1) & 0xFFF  # 12-bit mask
            if self.sequence == 0:
                timestamp = wait_for_next_ms(self.last_timestamp)
        else:
            self.sequence = 0

        if timestamp < self.last_timestamp:
            raise ClockMovedBackwardsError()  # Handle NTP skew!

        self.last_timestamp = timestamp

        return ((timestamp << 22) |
                (self.machine_id << 12) |
                self.sequence)

Database Index Impact: UUID v4 vs v7 Benchmark

MetricUUID v4 (random)UUID v7 (time-sorted)Snowflake (64-bit)Auto-increment
Insert throughput~5,000/sec~12,000/sec~15,000/sec~15,000/sec
Index size (1M rows)~80 MB~80 MB~40 MB~30 MB
Index fragmentationVery high (random)Low (sequential)Low (sequential)None
B-tree page splitsFrequentRare (append-only)RareNever
Distributed-friendlyYesYesYes (needs machine_id)No (SPOF)

Why UUID v4 is slow for inserts: B-tree indexes are optimized for sequential inserts (new rows go to the rightmost leaf). Random UUIDs cause inserts across all leaf pages, triggering constant page splits and cache misses. UUID v7 and Snowflake IDs are time-sorted, so inserts are always sequential — same performance as auto-increment.

Choosing the Right ID Strategy

Use CaseBest ChoiceWhy
Simple web app, single DBAuto-incrementSimplest, best performance
Microservices, needs coordination-freeUUID v7Time-sorted, no central authority
High-throughput (>100K IDs/sec)SnowflakeCompact (64-bit), sortable, fast
Public-facing URLsNanoID or hashidsShort, URL-safe, non-guessable
Globally unique, don''t care about sortingUUID v4Simplest distributed option
Short URL slugsBase62 encoded counterCompact, human-readable

Common Mistakes

[!TIP] Key Takeaways:
• Auto-increment fails in distributed systems (SPOF, cross-shard collisions).
• UUID v4: simple but not sortable and bad for DB indexes. Avoid as primary key.
• UUID v7: sortable, DB-friendly, no coordination. Best default choice for new systems.
• Snowflake: 64-bit, sortable, compact (4M IDs/sec/machine). Best for high-throughput systems.
• Clock skew is real. Never assume monotonic system time in distributed environments.
• Use NanoID or hashids for short, URL-safe, non-guessable public identifiers.

Open on QuizMaker