DevTools Hub
All articles

Development

UUID v1, v4 and v7 Explained: Which Identifier Should You Use?

How the different UUID versions are built, how they affect database performance, and when an auto-increment integer is still the better choice.

By DevTools Hub Team · · 7 min read

Sooner or later every application needs to give things an ID: users, orders, uploaded files, log entries. The two classic options are an auto-incrementing integer (1, 2, 3…) and a UUID like f47ac10b-58cc-4372-a567-0e02b2c3d479. And UUIDs themselves come in several versions that behave very differently. This article explains how they are built and how to choose.

Anatomy of a UUID

A UUID (Universally Unique Identifier) is a 128-bit number, written as 32 hexadecimal digits split into groups of 8-4-4-4-12. Two positions are special: the first digit of the third group is the version, and the first digit of the fourth group encodes the variant (almost always 8, 9, a or b for standard UUIDs).

f47ac10b-58cc-4372-a567-0e02b2c3d479
              ^    ^
        version 4  variant (a = RFC 9562)

The current standard is RFC 9562, published in 2024, which replaced the original RFC 4122 and formally added versions 6, 7 and 8.

Version 4: pure randomness

A v4 UUID is 122 random bits plus the six fixed version and variant bits. It is the most widely used version because it is simple and reveals nothing: no timestamps, no machine identifiers. The odds of a collision are astronomically small. You would need to generate around 2.7 × 1018 UUIDs to reach a 50% chance of a single duplicate.

The drawback appears in databases. Most relational databases store tables in a B-tree ordered by primary key. Random keys land all over the index, so every insert touches a different page, which causes page splits, poor cache usage and a bloated index. On small tables you won't notice; on tables with hundreds of millions of rows the slowdown can be significant.

Version 1: time plus node

v1 UUIDs combine a 60-bit timestamp (counted in 100-nanosecond intervals since 15 October 1582, the start of the Gregorian calendar), a clock sequence and a 48-bit node ID that was traditionally the machine's MAC address. They were designed to be unique without coordination, and they are, but they have two problems:

  • Privacy: a MAC-based v1 UUID reveals which computer generated it and exactly when. Modern generators, including ours, use a random node ID instead.
  • They don't sort by time. The timestamp's low bits come first in the string, so v1 UUIDs created a second apart look unrelated when sorted.

Version 7: the best of both

v7 is the version most new systems should consider. It puts a 48-bit Unix timestamp in milliseconds at the front of the UUID and fills the rest with random bits:

019a5f3c-8b2e-7d41-9c3a-5e6f7a8b9c0d
└─ ms timestamp ─┘ └─ random ────────┘

Because the most significant bits are a timestamp, v7 UUIDs:

  • sort in creation order, both as bytes and as strings;
  • insert at the end of a B-tree index, like an auto-increment integer, avoiding random-write overhead;
  • still contain 74 random bits, so they are unguessable enough for most purposes and safe to generate on many machines at once.

PostgreSQL 18 added a native uuidv7() function, and libraries exist for every major language. The one trade-off: anyone who sees a v7 UUID can read roughly when the record was created.

So should you just use integers?

Auto-increment integers are still a great choice in many cases. They are small (4 or 8 bytes versus 16), fast, human-friendly in logs and support tickets, and naturally ordered. Their weaknesses are:

  • They leak information. If your latest order is /orders/10452, a competitor knows roughly how many orders you have, and an attacker can try /orders/10451. This is the classic IDOR (insecure direct object reference) bug.
  • They need a central authority. Only the database can hand them out, which complicates offline apps, client-side ID generation and merging data from multiple databases.

A practical decision guide

SituationGood choice
Internal table, single database, IDs never exposedAuto-increment integer
IDs appear in URLs or APIsUUID v7 (or v4)
Large, write-heavy table needing UUIDsUUID v7
Creation time must not be revealedUUID v4
IDs generated on clients or across servicesUUID v7 or v4
Secret tokens (password reset, API keys)Not a UUID: use a dedicated 128+ bit random token

A popular hybrid is to keep an integer primary key for joins and performance, and add a separate UUID column that is the only identifier ever exposed publicly.

Storage tips

  • Store UUIDs in a native type (uuid in PostgreSQL, BINARY(16) in MySQL), not as a 36-character string. That cuts storage by more than half and speeds up comparisons.
  • Generate them with a cryptographically secure random source, never with a language's basic random().
  • Treat them as case-insensitive when parsing, but output lowercase.

Need some right now? Our UUID Generator creates up to 100 v4 or v1 UUIDs at once using the Web Crypto API, directly in your browser.

Keep reading