Using UUIDs as Database Primary Keys: Complete Guide
UUIDs solve real problems as primary keys — but they come with real costs too. Here's an honest breakdown of when they're worth it.
Why Developers Choose UUID Keys
- Distributed generation: Multiple application servers or microservices can generate valid IDs independently without any coordination or round-trip to a central sequence generator.
- No information leakage: Sequential integer IDs reveal record count and let anyone enumerate adjacent records by simply incrementing a URL parameter. UUIDs give no such hint.
- Merge-friendly: Combining data from multiple databases, replicas, or offline-synced clients is trivial with UUIDs since collision is virtually impossible, unlike auto-increment integers which will collide constantly when merged.
- Client-side ID generation: A mobile app can generate a permanent, valid record ID before ever syncing with the server — impossible with server-assigned auto-increment integers.
The Real Costs
| Factor | Auto-Increment Integer | UUID (v4) |
|---|---|---|
| Storage per key | 4-8 bytes | 16 bytes |
| Index size impact | Smaller, compact B-tree | Larger, and fragmented (see below) |
| Insert performance at scale | Fast — sequential append | Slower — random insertion position |
| Readable in URLs/logs | Yes, short and memorable | No, long and opaque |
Index Fragmentation Explained
Most relational databases store primary key indexes as B-trees, which perform best when new entries are inserted at the "end" — sequential integers naturally do this. A fully random UUID v4, by contrast, inserts at a random position within the tree on every single write, forcing frequent page splits and rebalancing. At small scale this is invisible; at hundreds of millions of rows with high write throughput, it becomes a measurable performance and storage bloat problem, particularly on traditional spinning-disk or even SSD-backed systems where write amplification matters.
How UUID v7 Changes the Calculus
UUID v7 was specifically designed to solve this exact problem. By embedding a millisecond-precision timestamp as the leading bits, newly generated v7 values are naturally close to sequential — new rows insert near the "end" of the index most of the time, dramatically reducing the fragmentation problem that plagues v4. This gives you most of the benefits of random UUIDs (distributed generation, no enumeration, merge-friendliness) while avoiding most of the index performance penalty. For new systems in 2026, v7 has largely closed the gap that used to make this a hard tradeoff.
Hybrid Approaches
Decision Framework
Use auto-increment integers when: you have a single database instance, don't need distributed ID generation, and want maximum insert performance and minimum storage overhead. Use UUID v4 when: enumeration prevention and generation independence matter more than raw insert performance, and your scale is moderate. Use UUID v7 when: you want the distributed-generation and privacy benefits of UUIDs without accepting v4's index fragmentation penalty — increasingly the best default for new systems in 2026.