← Back to blog

UUID vs Auto-Increment Database IDs

UUID vs Auto-Increment Database IDs

Choose between UUID v4 and auto-increment primary keys. Trade-offs for APIs, sharding, security, and database performance.

Two Philosophies for Primary Keys

Relational databases traditionally relied on auto-incrementing integers as primary keys: compact, ordered, and easy to type during debugging. Distributed systems and client-generated identifiers pushed many teams toward UUIDs—128-bit values standardized in RFC 4122 and updated by later UUID versions in RFC 9562. Neither choice is universally correct. The right answer depends on write volume, sharding strategy, privacy requirements, and whether clients must create IDs before talking to a central allocator. The UUID Generator on ToolsFree.org helps you inspect and generate identifiers while you prototype schema decisions.

Auto-increment values reveal approximate creation order and table size, which can leak business metrics or make scraping sequential objects trivial. UUIDs obscure sequence but cost more storage and can hurt index locality depending on version. Engineers often debate the topic with incomplete threat models. Frame the discussion around concrete workloads: single-region monoliths differ from multi-region event-sourced platforms. Document the decision in an ADR so future migrations are intentional rather than reactive.

Support engineers feel the difference daily: reading ticket notes with invoice 104829 is easier than pasting a UUID under stress, yet customer-facing portals should not expose those integers if enumeration is a concern. Provide copy buttons for UUIDs in admin UIs and teach agents to verify the full string carefully. Ergonomics belong in the decision matrix beside storage and locality. Ignoring human factors produces systems that are theoretically elegant and operationally frustrating for the people who keep production healthy during incidents.

Operational Strengths of Auto-Increment Integers

Integers are narrow, CPU-friendly, and produce naturally sorted indexes that keep recent inserts hot in buffer pools for many B-tree implementations. Humans can read them on support calls without copying thirty-six-character strings. Foreign keys stay small, which matters when a fact table collects billions of rows with multiple references. Sequential allocation is simple to reason about in backups and shard splits that key on ranges.

The downsides appear at scale and in security-sensitive APIs. Sequential public IDs enable enumeration attacks unless you add separate opaque tokens. Merging databases from two acquisitions with overlapping sequences requires careful remapping. Horizontal write scaling may need coordinated sequence generators or post-insert assignment that complicates offline-first clients. When those pains dominate, UUIDs or other opaque IDs deserve serious evaluation.

Benchmark both key types with production-like cardinality before a rewrite. Include secondary indexes, hot parent rows, and typical join patterns that mirror real traffic. Synthetic benchmarks on empty schemas hide the page-split behavior that dominates random UUID inserts at scale. Budget engineering time for measurement, not only for debate. Data from your own workload settles arguments faster than blog posts from unrelated companies with different sharding stories and hardware generations in other cloud regions.

  • Pros: compact indexes, human-friendly debugging
  • Pros: excellent B-tree locality for sequential inserts
  • Cons: enumerable in public APIs if exposed raw
  • Cons: harder multi-master allocation without coordination

UUID Versions and What They Imply

RFC 4122 popularized versions one through five, including time-based UUIDv1 and name-based UUIDv5. Random UUIDv4 became the default for many applications because it needs no central clock or namespace registry. Newer RFC 9562 defines UUIDv7, which embeds a Unix timestamp for time-ordered values that improve index locality while remaining widely unique. Choosing v4 versus v7 is often more important than “UUID versus integer” slogans suggest.

Generate samples with the UUID Generator and decode layout differences during design reviews. Avoid inventing proprietary string formats that break library validation. Store UUIDs in native UUID types when your database provides them, or in BINARY(16), rather than as ASCII strings that waste space and slow comparisons. Be consistent about casing and hyphenation in APIs; canonical textual form uses lowercase hex with hyphens.

-- PostgreSQL examples
CREATE TABLE events_v4 (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid()
);

CREATE TABLE events_v7 (
  id uuid PRIMARY KEY -- app supplies time-ordered UUID
);

Index Locality, Fragmentation, and Write Throughput

Random UUIDv4 inserts scatter across a B-tree index, increasing page splits and reducing cache efficiency compared with sequential keys. For write-heavy tables this can raise I/O and vacuum costs. Time-ordered UUIDs and ULID-like schemes mitigate the problem by keeping inserts append-friendly. Measure with realistic load tests rather than microbenchmarks on empty tables that hide fragmentation effects.

Read-heavy workloads that fetch by primary key alone may not notice locality issues. Analytical scans and range queries care more. If you shard by hash of the ID, randomness can help balance partitions. Match the identifier strategy to query patterns: sequential keys for chronologically clustered data, random keys when you need uniform distribution across shards. Revisit choices when traffic shapes change after product-market fit.

Client-Generated IDs and Offline-First Workflows

Mobile and edge clients often need an ID before connectivity returns so optimistic UI and outbox patterns work. Auto-increment cannot allocate offline without a reservation service. UUIDs shine here: clients generate an ID, enqueue writes, and reconcile later without colliding if the generator is sound. Pair client generation with server validation of format and optional signed tokens if spoofed IDs are a concern.

Idempotency keys should be distinct from primary keys in many APIs, yet UUID strings frequently serve both roles during early prototypes. Clarify semantics as you mature the platform. Use the UUID Generator in test setups to create stable fixtures, and the Hash Generator when you need deterministic fingerprints of payloads separate from identity. Keeping identity and integrity concepts separate prevents subtle bugs in replay handling.

Privacy, Enumeration, and Public Object References

Exposing auto-increment IDs in URLs such as /invoices/1024 invites neighbors to guess /invoices/1025. Even with authorization checks, enumeration reveals volume and timing. Opaque IDs reduce casual scraping, though they are not access control. Always authorize on every request. For highly sensitive resources, consider dual identifiers: internal integers and external opaque tokens rotated independently.

UUIDv1 can leak MAC address information in older generation schemes; prefer v4 or v7 for new systems unless you have a specific need. Log carefully: identifiers still appear in analytics and support tools. Teach staff not to treat obscurity as encryption. Combine opaque IDs with proper authentication, rate limiting, and audit trails for a defense-in-depth posture aligned with modern API security practice.

  • Never rely on ID obscurity as authorization
  • Prefer non-sequential public identifiers for enumerable resources
  • Avoid legacy UUIDv1 if MAC leakage is undesirable
  • Authorize every access regardless of ID type

Migration Strategies When You Outgrow Integers

Migrating a live table from integers to UUIDs is rarely a casual weekend task. Common patterns introduce a new UUID column, backfill, dual-write, switch reads, then drop the old key. Expand foreign keys carefully to avoid locking production. Some teams keep integers internally and add UUID external references without rewriting the entire schema—often the pragmatic compromise.

During migration, APIs may accept both forms temporarily. Document deprecation timelines and provide conversion endpoints for partners. Test backup and restore procedures with the new types. Use staging data that mirrors cardinality, not toy tables. Coordinate with analytics warehouses that already keyed facts on the old integer so downstream pipelines do not break silently.

Decision Checklist and Tooling on ToolsFree

Choose auto-increment when you have a single writer region, low enumeration risk, and a hunger for compact indexes. Choose UUID—especially time-ordered variants—when clients generate IDs, when you shard widely, or when public enumeration is unacceptable. Choose hybrid external tokens when internal integers remain operationally valuable. Write the rationale down with metrics you will revisit after twelve months of growth.

While prototyping, generate sample IDs with the UUID Generator, format related fixtures in the JSON Formatter, and browse other helpers on All Tools. ToolsFree.org keeps these utilities browser-local so schema experiments stay lightweight. The identifier debate is really a systems debate: measure locality, security, and developer ergonomics together instead of arguing slogans in isolation.

Side-by-side comparison of sequential integer IDs and UUID primary keys in a database schema

Generate RFC-compliant UUIDs for prototypes and test fixtures. UUID Generator →

Try our free tools

Apply what you learned — instant, browser-based.