DEV Community

Cover image for Stop using UUID v4 as your database primary key (and what to use instead)
Abu Sufyan
Abu Sufyan

Posted on • Originally published at wtkpro.site

Stop using UUID v4 as your database primary key (and what to use instead)

You've been generating UUID v4 for your primary keys. It works. Nothing is on fire. But your database is slowly getting slower, and you might not have noticed why yet.

Here's the short version: UUID v4 is completely random. That sounds like a good thing until you realise what it does to your B-tree index.


What's actually happening inside your database

Every time you insert a row with a random UUID v4 as the primary key, PostgreSQL (or MySQL, or any B-tree-indexed store) has to find a random spot in the index to place it. Not at the end — somewhere in the middle. Randomly. Every single time.

This causes index fragmentation. Over thousands of inserts, the index pages split constantly, fill unevenly, and require more I/O to read. On a small app you'll never notice. At 10M rows, you will.

Here's what a v4 primary key sequence looks like:

f47ac10b-58cc-4372-a567-0e02b2c3d479  ← random
3d4a2f1c-9b3e-4c7a-8d2e-1f5a9b3c7d2e  ← random
a1b2c3d4-e5f6-4789-abcd-ef0123456789  ← random
Enter fullscreen mode Exit fullscreen mode

Every one of those lands in a different page of your index. PostgreSQL has to do a page lookup every single insert.


UUID v7 fixes this

UUID v7 (standardised in RFC 9562) encodes a Unix millisecond timestamp in the first 48 bits. The rest is still random — you still get global uniqueness — but because the timestamp comes first, rows generated close in time sort close together in the index.

Here's what a v7 sequence looks like:

018f4b2c-1a00-7e3d-9b4f-2a1c3d5e7f9b  ← 2026-05-01 09:00:00.000
018f4b2c-1a01-7d2c-8a3e-1b2c4d6e8f0a  ← 2026-05-01 09:00:00.001
018f4b2c-1a02-7c1b-7b2d-0a1b3c5d7e9f  ← 2026-05-01 09:00:00.002
Enter fullscreen mode Exit fullscreen mode

Sequential. New rows go at the end of the index, not scattered through it. Index pages fill cleanly. Fragmentation drops dramatically.

The real-world impact: teams migrating from v4 to v7 primary keys have reported 30–60% reduction in index bloat on high-write tables. The write amplification on SSDs drops too.


When to use v4 vs v7

Situation Use
General-purpose unique ID (session tokens, correlation IDs, API responses) v4
Database primary key on a high-write table v7
You need IDs to be sortable by creation time v7
You want to obscure creation order from external users v4
Distributed system, multiple writers, no coordination v7 (timestamp still prevents fragmentation)

If you're starting a new project in 2026, default to v7 for primary keys. The only reason to pick v4 is when you explicitly want non-sortable IDs (rate-limiting tokens, password reset links, etc.).


Generating them in code

JavaScript / Node.js (v7):

// Node 22+ has experimental v7 support
// Until then, use the 'uuid' package
import { v7 as uuidv7 } from 'uuid';

const id = uuidv7();
// → '018f4b2c-dead-7bee-beef-123456789abc'
Enter fullscreen mode Exit fullscreen mode

PostgreSQL native (requires pg_uuidv7 extension):

SELECT uuid_generate_v7();
Enter fullscreen mode Exit fullscreen mode

Python:

import uuid_utils  # pip install uuid-utils

id = uuid_utils.uuid7()
print(str(id))
Enter fullscreen mode Exit fullscreen mode

Quick sanity check — extract the timestamp from a v7:

function extractTimestamp(uuidV7) {
  const hex = uuidV7.replace(/-/g, '').slice(0, 12);
  return new Date(parseInt(hex, 16));
}
Enter fullscreen mode Exit fullscreen mode

Need to generate a batch right now?

If you're seeding a database, writing a migration script, or just need a stack of test UUIDs without installing anything — I built a free browser-based tool that handles both v4 and v7 bulk generation.

👉 Bulk UUID v4 & v7 Generator — WebToolkit Pro

Everything runs in your browser via crypto.getRandomValues(). Nothing is sent to a server. Generate up to 100 at a time, copy them all in one click.


TL;DR

  • UUID v4 is random → random index positions → index fragmentation at scale
  • UUID v7 is time-prefixed → sequential inserts → cleaner indexes, faster writes
  • Use v7 for database primary keys, v4 for tokens and IDs that shouldn't be guessable
  • Native browser generation with crypto.getRandomValues() is already cryptographically secure — no library needed for v4; use the uuid package for v7 until runtimes catch up

Abu Sufyan is a full-stack developer and builder of WebToolkit Pro — a free, privacy-first collection of 150+ client-side developer utilities.

Top comments (0)