UUIDs are a go-to for developers building distributed systems. They’re globally unique, require no coordination, and work well across services.
But there’s a recurring complaint:
> “UUIDs destroy performance.”
Is it true?
Let’s dig into what UUIDs really do to your database — and how you can use them wisely without tanking performance.
🧬 What’s Inside a UUID?
A UUID is 128 bits (16 bytes), usually represented as a 36-character string with hyphens.
Common formats:
- UUIDv4: Fully random (122 bits of entropy)
- UUIDv7: Timestamp + random (lexicographically sortable)
How you store and index UUIDs plays a huge role in performance.
📦 Storage Comparison
Format | Size (bytes) | Type |
---|---|---|
UUIDv4 Text | 36 | CHAR(36) |
UUIDv4 Hex | 32 | CHAR(32) |
UUID Binary | 16 | BINARY(16) |
Native UUID | 16 | Postgres UUID |
Storing UUIDs as strings (36/32 characters) increases disk usage and index size — especially in large tables or write-heavy apps.
Best practice: use BINARY(16)
or native UUID column types.
⚡ Insert & Index Performance
Let’s benchmark insert + indexed read performance using:
- 10 million records
- Primary key on UUID
- UUIDv4 vs UUIDv7
CHAR(36)
vsBINARY(16)
vs native UUID
PostgreSQL Results
Type | Insert Speed | Index Scan Speed | Notes |
---|---|---|---|
UUIDv4 TEXT | ⚠️ Slow | ⚠️ Random access | Poor index locality |
UUIDv4 BIN | ✅ Fast | ✅ Fast | Efficient indexing |
UUIDv7 | ✅✅ Very fast | ✅✅ Very fast | Sequential-friendly write |
MySQL Results
Type | Insert Speed | Index Efficiency | Notes |
---|---|---|---|
CHAR(36) | ❌ Slow | ❌ High overhead | Bad for large tables |
BINARY(16) | ✅ Fast | ✅ Better indexes | Recommended format |
UUIDv7 (BIN) | ✅✅ Faster | ✅✅ Clustered ok | Better write patterns |
MongoDB (BSON/UUID Binary)
- MongoDB stores UUIDs as
BinData(4, ...)
internally - Performance is good as long as:
- Indexes are present
- IDs are not totally random (v7 > v4)
📉 The UUIDv4 Problem: Random Write Amplification
UUIDv4s are not ordered, which means every insert hits a random part of the B-Tree.
- Causes fragmentation
- Slows down inserts
- Hurts write-ahead log compression
Especially in clustered indexes (MySQL InnoDB), this leads to bad page locality.
🧠 Why UUIDv7 Is a Game-Changer
UUIDv7 includes a timestamp in the first 48 bits, making them:
- Lexicographically sortable
- Cluster-friendly
- Better for append-heavy tables
They offer the same uniqueness as v4, but with improved performance characteristics for both reads and writes.
🧪 Real-World Observations
Stripe’s ID Strategy
Stripe originally used auto-increment IDs, then moved to opaque, UUID-style strings. They optimized by:
- Using Base62 IDs with internal sortability
- Avoiding v4 collisions in logs and user-facing links
PlanetScale’s ULIDs
PlanetScale uses ULIDs (similar to UUIDv7) for replication-friendly keys with strong write performance and ordering.
✅ Best Practices
Storage
- PostgreSQL: use
UUID
column type (native) - MySQL: use
BINARY(16)
instead ofCHAR(36)
- MongoDB: use
BinData(4, ...)
, and index appropriately
Indexing
- Avoid UUIDv4 as clustered primary key
- Use UUIDv7/ULID for sequential writes
- If using UUIDv4, consider surrogate sequential IDs for clustering
Querying
- Don’t
LIKE '%uuid'
— always use full=
comparisons - Use indexed columns only — UUIDs are large, avoid scanning
🤓 FAQ
Should I stop using UUIDs?
No — but use them thoughtfully. UUIDs are not bad; poorly stored or indexed UUIDs are.
Are UUIDv7 and ULID supported in my DB?
- PostgreSQL: Custom UUID generators or
gen_random_uuid()
+ sort prefix - MySQL: Manual generation via app logic
- MongoDB: Store as strings or
BinData
Can I use UUIDs as public IDs?
Yes! UUIDv4 and UUIDv7 are great for public URLs, provided you avoid truncation or misuse.
Final Thoughts
UUIDs are fantastic tools — but they’re not one-size-fits-all. Used naively, they can hurt performance. Used wisely, they’re a superpower for distributed systems.
TL;DR:
- UUIDv4: fine for many cases, bad for indexing
- UUIDv7: better performance, more sortable
- Store as binary, not strings
- Optimize your schema — don’t blame the UUID
Use UUIDs. Just use them right. ⚙️