UUID Guide: When, Why, and How to Use Universally Unique Identifiers
TL;DR
A UUID (Universally Unique Identifier) is a 128-bit identifier that's practically guaranteed to be unique across all systems without coordination. UUIDv4 (random) is most common for general use. UUIDv7 (time-sortable) is emerging as the best choice for database primary keys. UUIDs are 36 characters in standard format: 550e8400-e29b-41d4-a716-446655440000.
Key Facts:
- UUIDs have 2^122 possible values (5.3 × 10^36)
- Collision probability is negligible (1 in 2.71 quintillion)
- UUIDv4 is purely random; UUIDv7 is time-based and sortable
- Standard format: 8-4-4-4-12 hexadecimal characters
What is a UUID?
A UUID (Universally Unique Identifier), also known as GUID (Globally Unique Identifier) in Microsoft ecosystems, is a 128-bit number used to identify information in computer systems.
Standard Format
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxxExample: 550e8400-e29b-41d4-a716-446655440000
| | | |
| | | +-- 12 hex chars
| | +------- 4 hex chars (variant)
| +------------ 4 hex chars (M = version)
+--------------------- 8 hex chars
- M indicates the version (1-8)
- N indicates the variant (8, 9, a, or b for RFC 4122)
---
UUID Versions Explained
UUIDv1: Timestamp + MAC Address
Time-based: Uses current timestamp and MAC address
Example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
| Pros | Cons |
|---|---|
| Sortable by time | Exposes MAC address (privacy) |
| Unique per machine | Can reveal generation time |
| Monotonically increasing | Requires coordination for same timestamp |
UUIDv4: Random
Fully random: 122 bits of randomness
Example: 550e8400-e29b-41d4-a716-446655440000
| Pros | Cons |
|---|---|
| No coordination needed | Not sortable |
| No privacy concerns | Random distribution in indexes |
| Simple to generate | Slightly worse DB performance |
UUIDv7: Time-Sortable (Recommended for Databases)
Unix timestamp prefix + random suffix
Example: 018e4f5a-5c00-7000-8000-000000000000
| Pros | Cons |
|---|---|
| Time-sortable | Newer standard (2022) |
| Better DB index performance | Less library support |
| No MAC address exposure | Reveals approximate generation time |
| Millisecond precision |
Other Versions
| Version | Method | Use Case |
|---|---|---|
| v2 | DCE Security | Rarely used |
| v3 | MD5 hash of namespace + name | Deterministic IDs |
| v5 | SHA-1 hash of namespace + name | Deterministic IDs |
| v6 | Reordered v1 for sorting | Sorting needs |
| v8 | Custom | Vendor-specific |
UUID vs Other Identifiers
UUID vs Auto-Increment Integer
| Aspect | UUID | Auto-Increment |
|---|---|---|
| Size | 16 bytes | 4-8 bytes |
| Uniqueness | Global | Database-local |
| Guessable | No | Yes (sequential) |
| Distributed generation | Yes | Requires coordination |
| Index performance | Lower (random) | Higher (sequential) |
| URL friendly | 36 chars | Short numbers |
UUID vs ULID
ULID (Universally Unique Lexicographically Sortable Identifier):Format: 01ARZ3NDEKTSV4RRFFQ69G5FAV
- 26 characters (Crockford Base32)
- Time-sortable
- 128 bits like UUID
| Aspect | UUID | ULID |
|---|---|---|
| Format | Hex with dashes | Base32, no dashes |
| Length | 36 characters | 26 characters |
| Sortable | v7 only | Always |
| Standardization | RFC 4122 | Less widespread |
UUID vs NanoID
NanoID: Compact, URL-friendly identifierFormat: V1StGXR8_Z5jdHi6B-myT
- Configurable length (default 21)
- URL-safe characters
- Similar collision probability
---
When to Use UUIDs
Good Use Cases
- Distributed systems - Generate IDs without central authority
- Database primary keys - Especially with UUIDv7
- API resources - Non-guessable resource identifiers
- Session tokens - Unpredictable session IDs
- File/object naming - Unique names without collisions
- Message/event IDs - Distributed event sourcing
When NOT to Use UUIDs
- Human-readable IDs - Use slugs or short codes instead
- URL slugs - Too long, use NanoID or custom
- When space matters - 16 bytes vs 4 bytes for int
- High-frequency generation - Consider snowflake IDs
- When you need sequential - Use auto-increment
UUID in Databases
PostgreSQL
-- Enable UUID extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";-- Create table with UUID primary key
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
email VARCHAR(255) NOT NULL
);
-- For UUIDv7 (PostgreSQL 17+)
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT uuidv7(),
created_at TIMESTAMP DEFAULT NOW()
);
MySQL
-- MySQL 8.0+
CREATE TABLE users (
id BINARY(16) PRIMARY KEY DEFAULT (UUID_TO_BIN(UUID())),
email VARCHAR(255) NOT NULL
);-- Or as CHAR (less efficient)
CREATE TABLE users (
id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
email VARCHAR(255) NOT NULL
);
MongoDB
// MongoDB uses ObjectId by default (12 bytes, time-sortable)
// But you can use UUID if needed
{
_id: UUID("550e8400-e29b-41d4-a716-446655440000"),
email: "user@example.com"
}
Index Performance Tips
- Use BINARY(16) instead of CHAR(36) in MySQL
- Use UUIDv7 for better index locality
- Consider composite indexes with UUID columns
- Test with realistic data volumes
Generating UUIDs in Code
JavaScript (Browser)
// Built-in (modern browsers)
const uuid = crypto.randomUUID();
// "550e8400-e29b-41d4-a716-446655440000"
Node.js
// Built-in (Node 14.17+)
const { randomUUID } = require('crypto');
const uuid = randomUUID();// Using uuid package
const { v4: uuidv4, v7: uuidv7 } = require('uuid');
const id = uuidv4();
const sortableId = uuidv7();
Python
import uuid
Version 4 (random)
id = uuid.uuid4()
UUID('550e8400-e29b-41d4-a716-446655440000')
Version 1 (timestamp + MAC)
id = uuid.uuid1()
Convert to string
str(uuid.uuid4()) # '550e8400-e29b-41d4-a716-446655440000'
Go
import "github.com/google/uuid"id := uuid.New()
fmt.Println(id.String())
PHP
// PHP 7.0+ with random_bytes
function uuid4() {
$data = random_bytes(16);
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}// Or use ramsey/uuid package
use Ramsey\Uuid\Uuid;
$uuid = Uuid::uuid4();
---
Validating UUIDs
Regex Pattern
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;function isValidUUID(str) {
return UUID_REGEX.test(str);
}
Common Validation Checks
- Length: Exactly 36 characters (with dashes)
- Format: 8-4-4-4-12 pattern
- Characters: Only hex digits and dashes
- Version: Position 15 indicates version (1-8)
- Variant: Position 20 is 8, 9, a, or b
Best Practices
DO:
- Use UUIDv7 for database PKs - Better performance than v4
- Store as BINARY(16) in MySQL for efficiency
- Use native types when database supports UUID
- Validate format on API inputs
- Consider ULID/NanoID for URLs
DON'T:
- Don't trust user-provided UUIDs - Always validate
- Don't use v1 if privacy matters - Exposes MAC address
- Don't assume uniqueness - Validate on insert if critical
- Don't use for short URLs - 36 characters is too long
- Don't expose internal IDs if security-sensitive
Quick Reference
Format Comparison
| Type | Length | Example |
|---|---|---|
| UUID | 36 chars | 550e8400-e29b-41d4-a716-446655440000 |
| UUID (no dashes) | 32 chars | 550e8400e29b41d4a716446655440000 |
| ULID | 26 chars | 01ARZ3NDEKTSV4RRFFQ69G5FAV |
| NanoID | 21 chars | V1StGXR8_Z5jdHi6B-myT |
Version Selection Guide
| Requirement | Version |
|---|---|
| General purpose | v4 |
| Database primary key | v7 |
| Deterministic from input | v5 |
| Time ordering needed | v7 or v1 |
| Legacy compatibility | v1 |
Free Tools
- UUID Generator → - Generate v1, v4, v7 UUIDs
- Hash Generator → - For deterministic IDs via hashing
Conclusion
UUIDs are essential for modern distributed systems where unique identifiers must be generated without coordination. UUIDv4 remains the most widely used for its simplicity, but UUIDv7 is becoming the preferred choice for database primary keys due to its time-sortable nature and better index performance. Key takeaways:
- UUIDv4 for general use, UUIDv7 for databases
- Store efficiently (BINARY vs CHAR)
- Consider alternatives (ULID, NanoID) for URLs
- Collision probability is negligible in practice
- Always validate format on untrusted input