If you have written code that generates IDs for database rows, API resources, or session tokens, you have almost certainly used — or at least considered — UUIDs. They appear everywhere in modern software: from PostgreSQL tables to REST API paths like /users/550e8400-e29b-41d4-a716-446655440000 to the tokens inside JWT headers.

But what exactly is a UUID, how does it work, and when should you use v4 versus v7 versus a plain integer? This guide answers all of that — plus the practical details developers need for database storage, code generation, and performance tuning.

What Is a UUID?

UUID stands for Universally Unique Identifier. It is a 128-bit label defined by RFC 4122 (and its successor RFC 9562) used to identify information in computer systems without a central authority.

A UUID is displayed as 32 hexadecimal digits split into five groups by hyphens in the 8-4-4-4-12 pattern:

550e8400-e29b-41d4-a716-446655440000
^^^^^^^^ ^^^^ ^^^^ ^^^^ ^^^^^^^^^^^^
   8      4    4    4       12

The third group encodes the version (the first hex digit: here 4), and the fourth group's first digit encodes the variant (a in RFC 4122 UUIDs means the high bits are 10).

UUID vs GUID — Are They the Same?

Yes. GUID (Globally Unique Identifier) is Microsoft's term for UUID. They follow the same RFC 4122 specification. The only cosmetic differences: Windows tools often display GUIDs in uppercase with curly braces — {550E8400-E29B-41D4-A716-446655440000}. When a .NET API or SQL Server asks for a GUID, a standard UUID works perfectly.

UUID Versions: v1, v3, v4, v5, and v7

The UUID standard defines multiple versions, each using a different strategy to generate uniqueness. Here is what each one does and when to use it.

Version 1 — Time + MAC Address

UUID v1 combines a 60-bit timestamp (100-nanosecond intervals since October 1582) with the MAC address of the generating machine. While guaranteed unique across time and space, v1 UUIDs expose both the host machine's MAC address and the exact creation timestamp — a privacy concern in user-facing contexts. They are rarely used in modern web applications for this reason.

Version 3 — Name-Based (MD5)

UUID v3 generates a deterministic UUID from a namespace UUID and a name string using MD5 hashing. Given the same inputs, v3 always produces the same UUID — useful when you need a consistent identifier for a given entity (e.g., always the same UUID for a given URL). Because MD5 is considered cryptographically weak, v5 is preferred over v3 for new implementations.

Version 4 — Random (Most Common)

UUID v4 uses 122 bits of cryptographically random data. The remaining 6 bits encode the version (4) and variant (RFC 4122). No machine info, no timestamp, no predictability — just randomness. This is the UUID type used in the vast majority of applications and what crypto.randomUUID() generates in JavaScript.

Collision probability: approximately 1 in 5.3 × 10³⁶. For context, generating a billion UUIDs per second for 85 years would give only a 50% chance of a single collision. For all practical purposes: treat it as zero.

Version 5 — Name-Based (SHA-1)

Like v3, but uses SHA-1 instead of MD5. Preferred over v3 for new code. Use v5 when you need the same UUID to be generated deterministically from a name — for example, generating a consistent UUID for each user email address or each product SKU. Not suitable as a security token since the output is predictable from the inputs.

Version 7 — Time-Ordered Random (New)

UUID v7 (RFC 9562, ratified 2024) embeds a Unix millisecond timestamp in the high bits, followed by random data. The result is a UUID that is both globally unique and monotonically increasing over time — rows inserted sequentially get sequentially ordered UUIDs. This solves the main performance problem with v4 in B-tree indexes (more on this below). Native support is arriving in ORMs and databases rapidly.

VersionBasisDeterministic?Time-ordered?Use when
v1Time + MACNoYesRarely — exposes MAC address
v3MD5 hashYesNoLegacy; prefer v5
v4RandomNoNoGeneral use — default choice
v5SHA-1 hashYesNoConsistent ID from a name
v7Time + RandomNoYesDatabase primary keys (new)

Generating UUIDs in Code

Every major language and database has first-class UUID support:

JavaScript / Node.js

// Built-in Web Crypto API (Node.js 14.17+, all modern browsers)
const id = crypto.randomUUID();
// → '550e8400-e29b-41d4-a716-446655440000'

Python

import uuid

# v4 (random)
id_v4 = uuid.uuid4()          # UUID object
str(id_v4)                     # '550e8400-...'

# v5 (name-based)
namespace = uuid.NAMESPACE_URL
id_v5 = uuid.uuid5(namespace, 'https://example.com')

PHP

// Using ramsey/uuid (composer require ramsey/uuid)
use Ramsey\Uuid\Uuid;
$id = Uuid::uuid4()->toString();

// Native (PHP 8.x, no library)
// No built-in v4, but you can use com_create_guid() on Windows
// or openssl_random_pseudo_bytes() cross-platform

PostgreSQL

-- Enable the extension (once per database)
CREATE EXTENSION IF NOT EXISTS "pgcrypto";

-- Generate a v4 UUID
SELECT gen_random_uuid();

-- Use as a column default
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    email TEXT NOT NULL
);

MySQL 8+

-- v1 UUID as string (default)
SELECT UUID();

-- Store efficiently as BINARY(16)
INSERT INTO users (id, email)
VALUES (UUID_TO_BIN(UUID(), 1), 'user@example.com');

-- Read back
SELECT BIN_TO_UUID(id, 1) AS id FROM users;

UUIDs as Database Primary Keys

Using UUIDs as primary keys is common in distributed systems, but there are important trade-offs to understand.

Why use UUIDs as primary keys?

  • No central coordination: Multiple application servers or database shards can generate IDs independently without collisions.
  • No sequential ID exposure: Integer primary keys leak information (total record count, insertion rate). UUIDs reveal nothing.
  • IDs can be generated client-side: The application can assign an ID before the database insert, simplifying workflows like optimistic UI updates.

The storage trade-off

Store UUIDs as BINARY(16) in MySQL or the native uuid type in PostgreSQL — not as VARCHAR(36). The hyphenated string form uses 36 bytes; BINARY(16) uses exactly 16 bytes. For a table with 10 million rows and several UUID columns, the difference is hundreds of megabytes of storage and significantly better index performance.

-- MySQL: efficient UUID storage
CREATE TABLE orders (
    id         BINARY(16)   PRIMARY KEY,
    user_id    BINARY(16)   NOT NULL,
    created_at DATETIME     NOT NULL
);

-- Insert (convert string → binary)
INSERT INTO orders (id, user_id, created_at)
VALUES (UUID_TO_BIN(UUID(), 1), UUID_TO_BIN('...', 1), NOW());

-- Query (convert binary → string for display)
SELECT BIN_TO_UUID(id, 1) AS id FROM orders;

The index fragmentation problem — and UUID v7

This is the biggest practical issue with UUID v4 as a primary key: because v4 values are completely random, new rows are inserted at random positions in the B-tree index. This causes page splits and index fragmentation — the index becomes scattered across many disk pages, degrading read and write performance over time.

The solution is to use time-ordered UUIDs:

  • UUID v7: Embeds a Unix millisecond timestamp in the high bits so new UUIDs sort after previous ones. The gold standard for new projects targeting RFC compliance.
  • ULID (Universally Unique Lexicographically Sortable Identifier): 128-bit, URL-safe, time-ordered alternative to UUID. Not RFC 4122-compliant but widely used.
  • UUID v1 with swap: Some MySQL-optimized approaches rearrange the timestamp bits of v1 to achieve time ordering. Covered in the MySQL 8.0 UUID support docs.

Frequently Asked Questions

Are UUID v4 collisions a real concern?
In practice, no. The collision probability is approximately 1 in 5.3 × 10³⁶. Generating a billion UUIDs per second for 85 years gives only a 50% chance of a single collision. Real-world applications will never hit a UUID collision — other failure modes (disk full, memory error, etc.) are astronomically more likely.
When should I use integers instead of UUIDs?
Use auto-incrementing integers when you have a single database node, do not need client-side ID generation, and want the smallest possible primary key (4 bytes for INT vs. 16 bytes for UUID). Integers also produce naturally ordered, gap-free sequences which simplifies some reporting queries. Use UUIDs when you need distributed ID generation, want to obscure record counts, or need to generate IDs client-side.
Can I use UUID as a URL parameter safely?
Yes. UUID v4 contains only hexadecimal characters and hyphens, all of which are URL-safe. No encoding needed: /users/550e8400-e29b-41d4-a716-446655440000 works as-is. The UUID does not reveal any information about the user or the system.
Does UUID v4 work offline or without a server?
Yes. UUID v4 generation is entirely local — it uses the device's cryptographically secure random number generator. No network call, no server, no central registry. This makes it ideal for client-side applications, offline-first apps, and distributed systems.

Key Takeaways

  • UUID v4 (random) is the default choice for most use cases — privacy-safe and universally supported.
  • UUID v7 (time-ordered) is the better choice for database primary keys — it maintains insertion order and prevents B-tree index fragmentation.
  • UUID and GUID are the same thing — GUID is just Microsoft's name for the same RFC 4122 standard.
  • Store UUIDs as BINARY(16) in MySQL or the native uuid type in PostgreSQL, not VARCHAR(36).
  • In JavaScript, use the built-in crypto.randomUUID() — no library required.
  • Use v5 (name-based, SHA-1) when you need the same UUID to be generated deterministically from a given input.

Ready to generate UUIDs? Use the UUID Generator to create single or bulk UUID v4 values instantly, copy or download them, and use them directly in your code or database.