UUID Best Practices for Databases: pitfalls, trade-offs, and how UUIDv7 improves common UUIDv4 pain points
UUIDs are easy to adopt and hard to optimize. Many teams switch from integer IDs to UUIDv4 for distributed generation, then discover slower inserts, fatter indexes, and inconsistent query behavior. This guide is meant to be a complete, practical reference for choosing UUID versions, designing schemas, and running safer migrations in production.[1][2][3][5]
Published 2026-03-03 · Updated 2026-03-03
Quick decision rule: if you are starting a new system and want globally unique IDs generated outside the database, UUIDv7 is usually the best default. Store IDs in native/binary form, measure index behavior under your actual workload, and avoid one-shot key migrations without a dual-write rollout.[1][2][3][5]
1) Why UUID strategy matters more than people expect
In architecture reviews, IDs are often treated like a detail. In production, they are not. Your identifier format sits in every table, every foreign key, every index, and every API payload. The wrong choice can increase write amplification, enlarge indexes, and make hot-path queries less predictable under load.
UUIDs solve real problems—especially decentralized generation and low collision risk across services. The key is choosing the right UUID version and storage strategy for your specific workload, not blindly using the first UUID library default.[1][2][3]
2) UUID versions: what changed with RFC 9562
RFC 9562 modernized UUID guidance and standardized additional versions including v6, v7, and v8. For engineering teams, the most important practical change is that UUIDv7 provides time-ordered UUIDs while preserving UUID compatibility.[1][6]
- UUIDv4: random-based. Strongly unique and simple, but random key ordering can hurt index locality at scale.
- UUIDv7: timestamp-forward with randomness. Often better for B-tree insertion behavior in real workloads.
- UUIDv6: reordered time-based format; useful in some migrations from v1 patterns but less common than v7 as a default recommendation.
3) Database pitfalls with UUIDv4 as a primary key
- Index page churn: random inserts distribute writes across many pages, which can increase page splits and reduce cache locality.[3]
- Larger on-disk footprint when stored as text: `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` text format is convenient for logs but inefficient for core storage.[2][5]
- Less predictable write latency under pressure: especially in systems where primary key ordering influences physical clustering.
- Migration risk: teams underestimate the complexity of converting primary keys tied to many foreign keys and background jobs.
4) Why UUIDv7 often performs better
UUIDv7 introduces time ordering in the high bits, which tends to produce insertion patterns that are closer to append-like behavior. In B-tree-backed indexes, this often means better page locality and fewer expensive random insert patterns compared with v4.[1][3]
It is not magic: your workload, fillfactor choices, checkpointing behavior, and replication settings still matter. But for many practical systems, UUIDv7 is a better starting point than UUIDv4 when IDs are indexed heavily.[1][4]
5) Storage and schema best practices (PostgreSQL + MySQL)
PostgreSQL
- Use the native UUID column type.
- Avoid storing UUIDs as `text` except for explicit display/export fields.
- For heavy-write indexes, evaluate index fillfactor and measure page split behavior in staging.
MySQL
- Prefer compact/binary storage patterns for UUIDs where appropriate.
- Use UUID conversion helpers consistently at write/read boundaries to avoid mixed formats.
- Keep API output readable (string UUID) while preserving efficient internal representation.
6) Migration playbook: UUIDv4 to UUIDv7 without downtime surprises
- Add a new nullable v7 column plus index in parallel.
- Dual-write new records (`id_v4` and `id_v7`) for an initial stabilization window.
- Backfill old rows in small batches with monitoring on replication lag and lock time.
- Update read paths and downstream consumers to accept v7.
- Migrate foreign keys iteratively, then swap PK roles in a controlled maintenance window.
- Leave rollback hooks until confidence is high, then remove legacy columns and code paths.
Teams usually fail here by under-scoping operational work, not by writing the wrong SQL. Treat this as an application migration, not just a schema migration.
7) UUIDv7 vs ULID vs KSUID
ULID and KSUID are both solid, widely used sortable ID formats. If your ecosystem already depends heavily on UUID parsing, UUID columns, and UUID-oriented tooling, UUIDv7 gives you sortable behavior while staying inside the UUID standards family.[1][7][8]
If you are on edge runtimes, check runtime capabilities; for example, some platforms now expose UUIDv7 directly in crypto APIs.[9]
8) FAQ
Is UUIDv4 wrong for databases?
No. UUIDv4 is still valid and secure for many systems. The issue is operational behavior under specific write-heavy and index-sensitive workloads where random key distribution can increase B-tree churn. If your workload is low-write or mostly append to non-clustered layouts, UUIDv4 may be perfectly acceptable.[1][3]
Why do teams prefer UUIDv7 now?
UUIDv7 keeps UUID semantics while introducing time-ordered bits. That often improves index locality and write behavior in practical systems while preserving decentralized ID generation.[1][6]
Should UUIDs be stored as text?
Usually no. Prefer native UUID columns (PostgreSQL) or compact binary representation where appropriate (for example BINARY(16) in MySQL) to reduce storage and index overhead.[2][5]
Do UUIDv7 IDs leak exact timestamps?
UUIDv7 includes time-derived information, so yes, they can reveal creation-time ordering at a coarse level. If this is sensitive for your use case, keep public identifiers decoupled or apply additional access controls.[1]
Sources
try uuid tool- [1] RFC 9562: Universally Unique IDentifiers (UUIDs)
- [2] PostgreSQL documentation: UUID type
- [3] PostgreSQL documentation: B-tree indexes
- [4] PostgreSQL documentation: Fillfactor
- [5] MySQL 8.4 Reference: UUID_TO_BIN / BIN_TO_UUID
- [6] IETF draft (historical context) for uuidrev / RFC 4122bis
- [7] ULID specification
- [8] KSUID project
- [9] Cloudflare docs: UUIDv7 support in Workers crypto API