Sign in to keep reading
“Unique ID Generation” is free with an account. Your progress and bookmarks follow you across devices.
Sign in to continueNEW: ML Mock & Coaching with a 20+ year experience coach. Save $20 on sessions for Premium.ML Mock & Coaching now available
NEW: ML Mock & Coaching with a 20+ year experience coach. Save $20 on sessions for Premium.ML Mock & Coaching now available
Generate globally unique identifiers in distributed systems using UUID, Snowflake, ULID, batch counters, and hash-based strategies.
“Unique ID Generation” is free with an account. Your progress and bookmarks follow you across devices.
Sign in to continueIn a single-database system, you get unique IDs for free. Set a column to AUTO_INCREMENT, and the database hands out 1, 2, 3, ... forever. Simple, sequential, and guaranteed unique.
But the moment you split into multiple databases, microservices, or sharded storage, that trick falls apart. There is no single authority to hand out sequential numbers anymore. Two services inserting at the same millisecond could both try to claim ID 42. You need a strategy that produces globally unique identifiers without a central coordinator — or at least without one that becomes a bottleneck.
This matters more than you might think. IDs show up everywhere in a distributed system: every message in Slack, every ride in Uber, every payment in a Payment system, every event in Google Calendar. Getting ID generation wrong means data corruption, duplicate records, or a throughput ceiling that chokes your entire platform.
Let us start with why the obvious solution breaks down. If every service calls a single database to get the next ID, that database becomes a funnel everything must pass through.
Every service must acquire a lock, increment the counter, and release the lock. At 10K IDs per second, you hit a wall — and adding more application servers only makes the contention worse, not better. This is the same fundamental problem discussed in Dealing with Contention: a shared mutable resource becoming a serialization point.
You have two paths forward: eliminate the central coordinator entirely (UUID, Snowflake, ULID) or reduce how often you need to talk to it (batch allocation).
The simplest distributed ID strategy is to generate random numbers that are so large, collisions are practically impossible. That is UUID v4 in a nutshell: 122 random bits, producing something like a3f2b1c4-9d8e-4f7a-b6c5-1a2b3c4d5e6f.
Any node can generate one independently, with no network calls and no coordination. The probability of a collision is about 1 in 2^122 — you would need to generate 2.7 quintillion UUIDs before having a 50% chance of a single duplicate.
So why doesn't everyone just use UUIDs? Two problems.
First, randomness kills database performance. B-tree indexes work best when new keys are appended to the end, which keeps pages sequential on disk. Random UUIDs scatter inserts across the entire index, causing page splits, fragmentation, and more disk I/O. For write-heavy tables, this can reduce insert throughput by 2-5x compared to sequential IDs. This connects directly to the storage engine trade-offs discussed in Scaling Writes — random writes are the enemy of B-trees.
Second, UUIDs are not time-ordered. If you want to query "show me the 50 most recent orders," a UUID-based primary key gives you no help. You need a separate indexed timestamp column, which means maintaining two indexes instead of one.
See it in practice: In File System, UUIDs work well for file and chunk identifiers because files are accessed by path, not scanned in insertion order. In Remote DevBox, session IDs are naturally random-access — nobody queries "give me the 100 most recent sessions in order."