Database Indexing & Primary Key Strategies: UUIDv4 vs. UUIDv7 vs. Auto-Increment

Database Indexing & Primary Key Strategies: UUIDv4 vs. UUIDv7 vs. Auto-Increment

In relational database design (PostgreSQL, MySQL InnoDB), choosing a primary key strategy is one of the most consequential architectural decisions an engineering team makes. The primary key defines the physical storage clustering order of rows on disk. Selecting the wrong primary key type causes catastrophic write amplification, massive B-tree index page splits, and severe performance degradation as datasets scale beyond available RAM.

Historically, software architects faced a harsh dilemma: utilize sequential auto-incrementing integers for optimal storage and write performance, or utilize randomly generated 128-bit UUIDv4 keys for global uniqueness across microservices at the expense of massive storage bloat and random disk I/O.

The Three Dominant Primary Key Strategies

Key Strategy Storage Size Sorting Nature B-Tree Write Performance Information Leakage Risk
Auto-Increment (BIGINT) 8 bytes Strictly sequential Maximum ($O(1)$ append-only) High (Enumeration & order volume leakage)
UUIDv4 (Random) 16 bytes (binary) / 36 bytes (string) Completely random Catastrophic (Frequent page splits) Zero (Unpredictable)
UUIDv7 (Time-Ordered) 16 bytes (binary) / 36 bytes (string) Monotonically time-ordered Near-optimal append write Zero (Cryptographically random tail)

Why UUIDv4 Destroys Clustered Index Performance

Relational databases such as MySQL InnoDB store table records inside a clustered index. The clustered index is physically ordered on disk by the primary key using a balanced B+ tree structure:

  1. With an auto-incrementing key, each new row is inserted directly into the rightmost leaf page of the B+ tree. The page fills sequentially until full, at which point a new page is allocated. Disk I/O is purely sequential.
  2. With a random UUIDv4 key, the key's leading hex digits are completely random. A newly inserted row can land in any arbitrary leaf page anywhere across the multi-gigabyte index.
  3. When an insert targets a leaf page that is already full, the database engine must execute an expensive page split: allocating a new page, moving half the rows over, and rewriting pointers in parent branch nodes.

As the table grows larger than the database buffer pool RAM, every single insert with UUIDv4 requires reading a random page from disk, causing write throughput to drop by 80% to 95%.

Enter UUIDv7: The Optimal Modern Standard (RFC 9562)

Standardized in RFC 9562, UUIDv7 combines the global distributed uniqueness of UUIDs with the sequential clustering advantages of auto-incrementing integers:

  • First 48 Bits: Unix epoch timestamp in milliseconds. Because time moves forward, newly created UUIDv7 keys are inherently sorted chronologically.
  • Next 12 Bits: Version identifier (0111) and sub-millisecond sequence counter to preserve strict monotonicity within identical millisecond timestamps.
  • Final 62 Bits: Cryptographically secure pseudo-random entropy from CSPRNG to guarantee global uniqueness across distributed server nodes.

MySQL / InnoDB Binary Storage Optimization

Never store UUIDs as 36-character VARCHAR(36) strings in production databases. A 36-byte string key quadruples index size and severely impairs cache hit rates. Store UUIDs as 16-byte binary columns:

-- Optimal MySQL UUIDv7 Table Architecture
CREATE TABLE orders (
    id BINARY(16) NOT NULL,
    customer_id INT UNSIGNED NOT NULL,
    amount DECIMAL(10, 2) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    KEY idx_customer (customer_id)
) ENGINE=InnoDB;

-- Inserting UUIDv7 into Binary Column
INSERT INTO orders (id, customer_id, amount) 
VALUES (UUID_TO_BIN('018e6e5a-7b3b-7000-8000-000000000001', 1), 42, 199.95);

-- Selecting and Converting Back to Text
SELECT BIN_TO_UUID(id, 1) AS id, customer_id, amount FROM orders WHERE customer_id = 42;

B-Tree Indexing Principles for High-Traffic Applications

Beyond primary key selection, secondary index performance dictates whether queries execute in sub-millisecond speeds or exhaust server connection pools:

  1. The Leftmost Prefix Rule: For compound indexes like KEY (status, created_at, user_id), the database can only use the index if the query filters on status first. A query filtering only on created_at cannot utilize the index.
  2. Covering Indexes: When all columns requested in a SELECT query are included within the secondary index itself, the database avoids reading the clustered index table page entirely (Index-Only Scan), speeding up queries by 5x to 10x.
  3. Index Cardinality: Low-cardinality columns (such as boolean flags or status columns with only 2 possible values) make poor standalone indexes. The query planner will typically ignore them in favor of full table scans.

Interactive Architecture Checklist

  • [ ] Replace random UUIDv4 primary keys with time-ordered UUIDv7 for high-throughput tables.
  • [ ] Store UUIDs as 16-byte binary (BINARY(16) / UUID) rather than text strings.
  • [ ] Profile slow queries using EXPLAIN ANALYZE to verify index-only scans.
  • [ ] Generate RFC-compliant UUIDs client-side using our free UUID Generator.
Muhammad Sabir Uppal

Written by Muhammad Sabir Uppal

Lead Software Engineer & Founder of TU Web Tools

Muhammad is a full-stack software engineer specializing in browser-based developer tools, high-performance PHP architecture, and technical SEO. He builds zero-dependency, privacy-first web utilities and publishes in-depth engineering guides to help developers and businesses work faster.

Try the tools mentioned in this guide

All TU Web Tools utilities are free, browser based and need no signup.

Browse all tools

Related articles