UUID Explained: What It Is, How It Works, and When to Use It
Every database row needs an identifier. The simplest approach is an auto-incrementing integer (1, 2, 3…), but this breaks down when you have multiple databases, need to generate IDs client-side, or want to avoid exposing sequential record counts to users. This is where UUIDs come in.
UUID stands for Universally Unique Identifier. It is a 128-bit identifier that can be generated independently on any machine without any coordination — and still be virtually guaranteed to be unique across the entire world.
What a UUID Looks Like
A UUID is 32 hexadecimal digits formatted in five groups separated by hyphens:
The format is 8-4-4-4-12 characters, always 36 characters total (including hyphens). The third group encodes the UUID version — in the example above, the 4 at position 15 indicates version 4.
UUID Versions
RFC 4122 defines five UUID versions, each generated differently:
| Version | Method | Common Use |
|---|---|---|
| v1 | Time + MAC address | Time-sortable IDs, legacy systems |
| v2 | Time + MAC + DCE security | Rarely used |
| v3 | MD5 hash of namespace + name | Deterministic IDs from names |
| v4 | Random (122 bits of randomness) | General purpose — most common |
| v5 | SHA-1 hash of namespace + name | Deterministic IDs (preferred over v3) |
In practice, UUID v4 is what almost everyone uses. It is purely random, requires no network or time input, and its 122 bits of entropy make collisions statistically impossible in any real system.
UUID v1: Time-Based
UUID v1 encodes a 60-bit timestamp (in 100-nanosecond intervals since October 15, 1582) plus the MAC address of the generating machine. This makes v1 UUIDs sortable by creation time — which has performance benefits for database indexing (sequential IDs lead to fewer B-tree page splits).
The downside: v1 UUIDs leak information. The MAC address identifies the machine that generated the ID, and the timestamp reveals exactly when it was created. For user-facing IDs (like in URLs), this can be a privacy and security concern.
UUID v4: Random
UUID v4 is 128 bits, of which 6 bits are used for version/variant markers — leaving 122 bits of randomness. That's 2122 possible values, or approximately 5.3 × 1036.
To put the collision probability in perspective: if you generated 1 billion UUIDs per second for the next 85 years, the probability of any collision would be roughly 50%. For any normal application generating millions of IDs, collisions are not a realistic concern.
// Example UUID v4 values: f47ac10b-58cc-4372-a567-0e02b2c3d479 6ba7b810-9dad-11d1-80b4-00c04fd430c8 a8098c1a-f86e-11da-bd1a-00112444be1e
Generating UUIDs in JavaScript
Modern browsers and Node.js (v14.17+) include a built-in crypto.randomUUID() method — no library needed:
// Browser and Node.js 14.17+
const id = crypto.randomUUID();
// → "550e8400-e29b-41d4-a716-446655440000"
// Node.js (destructured)
const { randomUUID } = require('crypto');
const id = randomUUID();
For older environments, the popular uuid npm package covers all versions:
import { v4 as uuidv4, v1 as uuidv1, v5 as uuidv5 } from 'uuid';
uuidv4(); // random v4
uuidv1(); // time-based v1
uuidv5('hello', uuidv5.DNS); // deterministic v5 from name
Generating UUIDs in Python
Python's standard library includes a uuid module:
import uuid # UUID v4 (random) print(uuid.uuid4()) # → 550e8400-e29b-41d4-a716-446655440000 # UUID v1 (time-based) print(uuid.uuid1()) # UUID v5 (deterministic from name) print(uuid.uuid5(uuid.NAMESPACE_DNS, 'example.com')) # Access as string or components u = uuid.uuid4() print(str(u)) # full string with hyphens print(u.hex) # 32 hex chars, no hyphens print(u.int) # integer representation
UUID vs Auto-Increment IDs
| Aspect | UUID v4 | Auto-Increment Integer |
|---|---|---|
| Uniqueness | Globally unique, no coordination needed | Unique only within one table/sequence |
| Client-side generation | Yes — any client can generate a valid ID | No — requires a round-trip to the database |
| Merging databases | Safe — UUIDs don't conflict | Conflicts must be manually resolved |
| URL exposure | Opaque — doesn't reveal record count | Sequential — exposes total count |
| Storage size | 16 bytes (binary) or 36 chars (string) | 4 bytes (INT) or 8 bytes (BIGINT) |
| Index performance | Random = scattered inserts, more page splits | Sequential = append-only, efficient B-tree |
| Readability | Hard to remember or type | Short, memorable |
When to Use UUIDs
- Distributed systems — multiple services or clients generate IDs without coordination
- Microservices — each service creates its own records and IDs must be unique across services
- Client-generated IDs — mobile apps or frontends that create records offline (and sync later)
- Multi-tenant databases — merging data from multiple tenant databases without ID conflicts
- Hiding record counts — don't want users to guess that user #5 means you have only 5 users
- Event sourcing / CQRS — events need stable IDs that don't depend on DB insertion order
When to Stick with Auto-Increment
- Simple single-database applications — no distribution, no merging needed
- High-write workloads on indexed columns — sequential IDs are significantly faster for B-tree indexes
- Human-facing IDs — order numbers, ticket numbers, short IDs users might type or say aloud
- Storage-constrained environments — a 4-byte INT vs a 16-byte UUID matters at scale
BINARY(16) (store as bytes, not a string) and consider UUID v1 or an ordered UUID variant (like UUIDv7, which embeds a sortable timestamp). Random UUIDs as string primary keys cause significant write amplification in InnoDB.
UUIDv7 — The New Standard
RFC 9562 (2024) introduced new UUID versions. UUID v7 is gaining adoption fast: it embeds a millisecond-precision Unix timestamp in the high bits, making it time-sortable like v1 but using random data (not MAC addresses) for the remaining bits. This combines the best of both worlds — sortable for database indexing, random enough to be unpredictable.
// UUIDv7 (Node.js, using uuid@9+ package)
import { v7 as uuidv7 } from 'uuid';
console.log(uuidv7());
// → 018f4133-4300-7f3e-9c4a-4b5f5d12345a
// ^^^^^^^^^^^^ timestamp prefix — sortable!
If you're starting a new project in 2025 and plan to use UUIDs as primary keys, UUID v7 is worth serious consideration.
Storing UUIDs in Databases
- PostgreSQL: has a native
UUIDtype — use it. Stores as 16 bytes, supports indexing and comparison natively. - MySQL/MariaDB: no native UUID type. Use
BINARY(16)withUUID_TO_BIN()andBIN_TO_UUID()functions (MySQL 8+), orCHAR(36)if readability matters more than performance. - SQLite: store as
TEXT(36 chars) orBLOB(16 bytes). - MongoDB: use the native
UUIDBSON subtype, or store as a string.
Frequently Asked Questions
What is a UUID?
A UUID (Universally Unique Identifier) is a 128-bit identifier standardized by RFC 4122, written as 32 hexadecimal digits in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. They are designed to be globally unique without central coordination — any machine can generate one and it will not conflict with any other UUID ever generated.
What is the difference between UUID v1 and UUID v4?
UUID v1 is time-based — it encodes the current timestamp and the generating machine's MAC address. This makes it sortable by creation time, but it leaks information about when and where it was created. UUID v4 is randomly generated (122 bits of randomness), making it suitable for most applications where privacy and unpredictability matter.
Can two UUIDs ever be the same?
In theory, yes — UUID v4 has 2122 possible values. In practice, the probability of a collision is negligible: generating 1 billion UUIDs per second for 85 years gives you about a 50% chance of one collision. For any normal application, UUID collisions are not a realistic concern.
What is the difference between UUID and GUID?
They are the same thing. GUID (Globally Unique Identifier) is Microsoft's term for the same concept. A GUID is a UUID — same 128-bit format, same RFC 4122 standard. The terminology differs by ecosystem: UUID is used in open standards; GUID is used in Windows/.NET.