UUID and GUID Explained: A Developer's Guide to v1, v4, and v7
You've seen them everywhere — in database primary keys, in API responses, in URLs, in log files. A 36-character string of hex digits and hyphens like 550e8400-e29b-41d4-a716-446655440000. They're called UUIDs (or GUIDs, depending on who you ask), and despite being everywhere, most developers couldn't tell you the difference between v1, v4, and v7 — or why it matters.
This guide explains how UUIDs work, what each version is for, the surprising performance trap that v4 has in databases, and why UUID v7 is the version you should probably be using in 2026.
What Is a UUID?
A UUID (Universally Unique Identifier) is a 128-bit number designed to be globally unique without needing a central authority. It's typically displayed as 32 hexadecimal characters split into five groups by hyphens:
550e8400-e29b-41d4-a716-446655440000
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
8 4 4 4 12 digits
M = version (1, 4, 7, etc.)
N = variant (almost always 8, 9, A, or B)
The "M" position encodes the version (which generation algorithm produced it), and the "N" position encodes the variant (how to interpret the rest of the bits). The other 122 bits carry the actual identifier data, which differs from version to version.
UUID vs GUID — Are They Different?
No. GUID (Globally Unique Identifier) is Microsoft's name for the same thing. Microsoft adopted UUIDs early and stuck with their own terminology in COM, .NET, and SQL Server. Outside the Windows ecosystem, the term is UUID. The bits are identical.
The only nuance: Microsoft's GUID display sometimes wraps the value in braces — {550e8400-e29b-41d4-a716-446655440000} — while everyone else omits them.
The UUID Versions That Matter
The UUID spec defines eight versions (v1 through v8), but only a handful are in real-world use:
| Version | Based on | Sortable | Common use |
|---|---|---|---|
| v1 | MAC address + timestamp | Partial | Legacy systems |
| v3 | MD5 hash of a name | No | Deterministic IDs |
| v4 | Random | No | Most common today |
| v5 | SHA-1 hash of a name | No | Deterministic IDs |
| v7 | Unix timestamp + random | Yes | New default for DBs |
UUID v4 — Random Bits
UUID v4 is the workhorse. 122 of its 128 bits come from a cryptographically secure random source, with the other 6 bits reserved for version and variant. Generated like this in most languages:
// JavaScript (Node 19+, modern browsers)
crypto.randomUUID()
// → "f47ac10b-58cc-4372-a567-0e02b2c3d479"
// Python
import uuid
uuid.uuid4()
// Go
import "github.com/google/uuid"
uuid.NewRandom()
// Swift
UUID() // generates v4 by default
// Ruby
require 'securerandom'
SecureRandom.uuid
Collision Odds
Because v4 is essentially a 122-bit random number, collisions are astronomically unlikely. To have a 50% chance of producing a duplicate, you'd need to generate 2.71 × 1018 UUIDs — about 2.7 quintillion. If your application generated one billion UUIDs every second, you'd need to run for 86 years before reaching even a 1-in-a-billion collision risk.
For practical purposes, v4 collisions don't happen. You can treat them as unique forever.
The v4 performance trap: Random UUIDs as primary keys hurt write performance in B-tree-indexed databases (PostgreSQL, MySQL/InnoDB, SQL Server). Random inserts cause page splits and index fragmentation, which in heavy write workloads can cut throughput by 50% or more. This is the reason UUID v7 exists.
UUID v7 — Time-Ordered, Database-Friendly
UUID v7 was published in RFC 9562 (May 2024) specifically to fix the database problem with v4. Its layout:
0190d8c4-7e0e-7c39-9b1f-3a4b5c6d7e8f
└─── 48-bit ms ───┘ M └── 74 random ──┘
Unix timestamp version
First 48 bits: Unix epoch in milliseconds (sortable!)
Next 4 bits: Version "7"
Remaining: Variant + 74 bits of randomness
Because the timestamp is at the front, v7 UUIDs sort chronologically when ordered as strings or bytes. Inserting them into a B-tree index appends to the end — the same write pattern as auto-incrementing integers, but with the global uniqueness of a UUID.
Generating UUID v7
// JavaScript (uuid 9.0+)
import { v7 as uuidv7 } from 'uuid';
uuidv7()
// → "0190d8c4-7e0e-7c39-9b1f-3a4b5c6d7e8f"
// Python (uuid-utils, or stdlib in Python 3.13+)
import uuid
uuid.uuid7() // Python 3.13+
// PostgreSQL 18+
SELECT uuidv7();
// Go (google/uuid v1.6+)
uuid.NewV7()
Use v7 for new database tables. You get the operational benefits of UUIDs (no central counter, safe to generate offline, no exposing row counts) without paying the write-performance penalty of v4. If your DB or runtime supports v7, prefer it.
UUID v1 — The Original
UUID v1 was the first version, dating back to the 1980s. It combines:
- 60-bit timestamp — 100-nanosecond intervals since 1582-10-15 (Gregorian calendar reform)
- 14-bit clock sequence — randomised at startup to avoid collisions during clock changes
- 48-bit node ID — originally the machine's MAC address
Like v7, v1 is timestamp-based, but it's been superseded in modern systems for two reasons. First, embedding the MAC address leaks the originating machine's hardware identity — a privacy concern. Second, v1's timestamp byte order is shuffled in a way that defeats lexicographic sorting, so it doesn't actually give you the ordered-insert benefits that v7 does.
UUID v3 and v5 — Deterministic IDs
v3 and v5 are name-based: given the same namespace and name, they always produce the same UUID. v3 hashes with MD5; v5 hashes with SHA-1.
import uuid
NAMESPACE = uuid.NAMESPACE_URL
uuid.uuid5(NAMESPACE, "https://example.com/users/42")
# → always the same UUID for that URL
Useful when you want a stable identifier derived from a known input — for example, a content-addressable cache key, or a deterministic ID for a third-party resource you're mirroring.
ULID, NanoID, and Other Alternatives
If you don't need strict UUID compatibility, alternative ID schemes exist:
| Format | Length | Sortable | Notes |
|---|---|---|---|
| UUID v4 | 36 chars (32 hex) | No | Universal support |
| UUID v7 | 36 chars | Yes | Newer, time-ordered |
| ULID | 26 chars (Crockford Base32) | Yes | Pre-dates UUID v7; same idea |
| NanoID | 21 chars (URL-safe) | No | Shorter, URL-friendly |
| KSUID | 27 chars (Base62) | Yes | 32-bit timestamp + 128-bit random |
| Snowflake | 64-bit integer | Yes | Twitter; needs central coordination |
For interop with the wider ecosystem — databases, UUID columns, JSON Web Tokens, OAuth scopes — stick with UUIDs. ULID/NanoID/KSUID are reasonable choices when you control the full stack and want shorter strings.
UUIDs in Databases
PostgreSQL
-- Native UUID type
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- v4
-- or, in Postgres 18+:
id UUID PRIMARY KEY DEFAULT uuidv7(),
name TEXT NOT NULL
);
MySQL / MariaDB
-- Store as BINARY(16), not CHAR(36) — saves 60% space
CREATE TABLE users (
id BINARY(16) PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
INSERT INTO users VALUES (UUID_TO_BIN(UUID()), 'Paul');
SQLite
SQLite has no native UUID type. Store as TEXT (36 chars) or BLOB (16 bytes). For BLOB, you save space and gain faster comparison, at the cost of less convenient debugging.
When NOT to Use UUIDs
- Public-facing IDs you want short. A 36-character ID in a URL is ugly. Consider a short slug or a NanoID instead.
- Tiny tables. If your table has 100 rows, a UUID primary key is overkill. Auto-incrementing integers are simpler and faster.
- Distributed counters. If you need a "user #4,729" type identifier where the number itself is meaningful, UUIDs don't help — use a real counter.
- Cryptographic tokens. UUIDs are unique, not unguessable. For session tokens, password reset tokens, or API keys, generate cryptographically secure random bytes (32+ bytes) and Base64-encode them.
Quick Decision Guide
- New project, modern database? → UUID v7
- Working in legacy code or unsure? → UUID v4 (universal, safe)
- Need a deterministic ID from input? → UUID v5
- Need shorter strings, controlled stack? → ULID or NanoID
- Need session/auth tokens? → Don't use UUIDs — use a secure random token
Inspect IDs and Tokens On Your Phone
BoltKit bundles 10 essential developer tools — JWTInspect for tokens, Base64Lab for encoding, JSONPretty for API responses, and more. Free on iPhone, iPad, and Mac.
Get BoltKit Free