DEV Community

Cover image for ๐Ÿš€ UUIDv4 vs UUIDv7 in PostgreSQL
Fazal Mansuri
Fazal Mansuri

Posted on

๐Ÿš€ UUIDv4 vs UUIDv7 in PostgreSQL

Why Switching UUID Versions Can Boost Performance at Scale

Many teams choose UUIDs as primary keys to support distributed systems and avoid ID collisions.
But not all UUIDs are equal.

Some teams have seen 10ร—โ€“50ร— performance improvements simply by switching from UUIDv4 to UUIDv7.

Sounds surprising?
Letโ€™s break it down - step by step - with no hand-waving.


๐ŸŽฏ What This Blog Will Help You Decide

By the end, youโ€™ll understand:

  • What UUIDv4 and UUIDv7 actually are
  • How PostgreSQL indexes work internally
  • Why random IDs hurt performance
  • Why UUIDv7 scales dramatically better
  • When you should (and shouldnโ€™t) switch
  • How to benchmark this yourself

1๏ธโƒฃ Quick Refresher: What Is a UUID?

A UUID (Universally Unique Identifier) is a 128-bit value designed to be globally unique.

Why developers use UUIDs:

  • Safe for distributed systems
  • Can be generated client-side
  • No central ID generator
  • Avoids ID collisions across services

PostgreSQL stores UUIDs efficiently (16 bytes), so UUIDs themselves are not slow.

๐Ÿ‘‰ The problem is the insertion pattern, not the type.


2๏ธโƒฃ UUIDv4 - Random by Design

How UUIDv4 Works

UUIDv4 is purely random (122 random bits).

Example:

9f1c0d3a-4b6e-4b8f-8f91-17e0a0c9a721
e2a31f8b-91f2-4df7-98cb-6e9fcae721aa
0d2c5f99-5d8c-4b6d-8a0e-2dbbfa17d441
Enter fullscreen mode Exit fullscreen mode

Key properties:

  • No ordering
  • No timestamp
  • Completely scattered values

This randomness is great for uniqueness
โ€ฆbut terrible for database indexes.


3๏ธโƒฃ UUIDv7 - Time-Ordered UUIDs

UUIDv7 is a newer standard designed specifically for modern databases.

How UUIDv7 Works

UUIDv7 combines:

  • A timestamp (milliseconds) in the most significant bits
  • Random bits for uniqueness

Example (simplified):

018f3b3a-9b5c-7d12-bc01-9a2cfe123456
018f3b3a-9b5d-7f44-a231-acde441298ab
018f3b3a-9b5e-82a1-9d22-acde112341aa
Enter fullscreen mode Exit fullscreen mode

Key properties:

  • Mostly increasing over time
  • Still globally unique
  • Still safe for distributed systems

๐Ÿ‘‰ This ordering changes everything.


4๏ธโƒฃ How PostgreSQL Stores and Indexes Data (Critical Part)

PostgreSQL Table Storage

  • Rows are stored in a heap
  • Order in heap is not guaranteed

PostgreSQL Indexes

Primary keys use B-tree indexes by default.


5๏ธโƒฃ What Is a B-Tree (In Simple Terms)

A B-tree:

  • Stores keys in sorted order
  • Is optimized for:

    • Sequential inserts
    • Range scans
    • Cache-friendly access

Think of it like:

A well-organized book where new pages are best added at the end.


6๏ธโƒฃ Why UUIDv4 Hurts PostgreSQL Performance

With UUIDv4:

  • Each new ID belongs somewhere random in the index
  • PostgreSQL must:

    • Find the correct position
    • Split pages frequently
    • Rebalance the tree
    • Touch random memory pages

Consequences:

  • Heavy page splits
  • Index fragmentation
  • Poor cache locality
  • Increased disk I/O
  • Slower inserts
  • Slower reads
  • Slower vacuum

This gets worse as the table grows.


7๏ธโƒฃ Why UUIDv7 Is Fast

With UUIDv7:

  • New rows are mostly appended
  • Inserts hit the rightmost leaf page
  • Minimal page splits
  • Excellent cache locality
  • Sequential disk writes

This is very similar to:

  • BIGSERIAL
  • IDENTITY
  • Snowflake-style IDs

๐Ÿ‘‰ PostgreSQL loves this pattern.


8๏ธโƒฃ Why Teams See 10ร—โ€“50ร— Improvements

At scale (millions of rows):

Metric UUIDv4 UUIDv7
Insert latency High Low
Index size Large Smaller
Cache efficiency Poor Excellent
Page splits Frequent Rare
Vacuum cost High Lower

โš ๏ธ Important note:

  • 50ร— is workload-dependent
  • Typical gains are 5ร—โ€“20ร—
  • Still very significant

9๏ธโƒฃ Hands-On Benchmark (You Can Try This)

Table Setup

CREATE TABLE test_uuid_v4 (
  id UUID PRIMARY KEY,
  data TEXT
);

CREATE TABLE test_uuid_v7 (
  id UUID PRIMARY KEY,
  data TEXT
);
Enter fullscreen mode Exit fullscreen mode

Insert UUIDv4

INSERT INTO test_uuid_v4
SELECT gen_random_uuid(), 'data'
FROM generate_series(1, 1000000);
Enter fullscreen mode Exit fullscreen mode

Insert UUIDv7 (Postgres 16+ or extension)

INSERT INTO test_uuid_v7
SELECT uuidv7(), 'data'
FROM generate_series(1, 1000000);
Enter fullscreen mode Exit fullscreen mode

UUIDv7 generation requires Postgres 18 (uuidv7()) or an extension such as pg_uuidv7 in earlier versions.

Observe:

  • Insert time
  • Index size
  • CPU usage
  • Disk writes

Youโ€™ll see:

  • UUIDv7 inserts are dramatically smoother
  • Index size is smaller
  • Less I/O pressure

๐Ÿ” Why This Matters in Real Systems

Backend Impact

  • Faster writes
  • Better read performance
  • Healthier indexes
  • Lower infrastructure cost

Frontend Impact

  • IDs sort naturally by creation time
  • Easier pagination
  • Better caching behavior
  • Cleaner URLs

โš ๏ธ When UUIDv7 Is NOT the Right Choice

Be honest and balanced:

  • If you need fully random IDs
  • If timestamp leakage is a concern
  • If your dataset is small
  • If sequential IDs are unacceptable

UUIDv7 is a performance trade-off, not magic.


๐Ÿ Final Thoughts

This performance boost isnโ€™t accidental โ€” itโ€™s how databases work.

The takeaway:

  • PostgreSQL B-trees love ordered inserts
  • UUIDv4 is random โ†’ bad at scale
  • UUIDv7 is time-ordered โ†’ excellent at scale
  • Schema design decisions matter more than hardware

Choosing the right ID strategy early can save years of performance tuning later.


๐Ÿ’ฌ Have you used UUIDv4 in production and faced scaling issues?
Or already switched to UUIDv7?

Letโ€™s discuss โ€” real-world experiences help everyone.


Top comments (0)