← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

UUID vs ULID vs Auto-Increment

Database PHP 7.0+ Intermediate
debt(d7/e5/b7/t7)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). The term's detection_hints lists percona-toolkit and mysql-slow-query-log, both runtime/operational tools rather than static analysis. Index fragmentation from UUID v4 only manifests under load, and auto-increment enumeration vulnerabilities require manual security review. No automated static tool catches these patterns at development time.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix suggests switching from UUID v4 to UUID v7/ULID, but this requires migrating existing data, updating schema definitions, changing ID generation code, and potentially modifying foreign key relationships. On large tables, this is a significant migration effort within the database layer, not a one-line fix.

b7 Burden Structural debt — long-term weight of choosing wrong

Closest to 'strong gravitational pull' (b7). Primary key strategy is a foundational database design choice that affects every table, every foreign key relationship, and every query pattern. The term applies_to all PHP contexts (web, cli, queue-worker), indicating system-wide reach. Once you've committed to UUID v4 on high-write tables, the fragmentation tax is paid on every INSERT and the migration cost shapes future decisions.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception explicitly states that UUID v4 is commonly assumed to be the best choice for globally unique IDs, when its randomness actually causes severe B-tree index fragmentation. This contradicts intuition from other ecosystems where UUIDs are recommended without performance caveats. The common_mistakes list reinforces multiple non-obvious gotchas: VARCHAR storage, auto-increment enumeration, and ULID sorting issues.

About DEBT scoring →

Also Known As

UUID ULID primary key UUID v7 auto-increment

TL;DR

Primary key strategies: auto-increment is simple and fast, UUID v4 is globally unique but random (poor index performance), UUID v7 and ULID are sortable globally unique IDs.

Explanation

Auto-increment (BIGINT): simple, compact, sequential — poor for distributed systems (coordination needed). UUID v4: globally unique, no coordination, but fully random — causes B-tree index fragmentation and poor cache locality. UUID v7 (2023): UUID format with millisecond timestamp prefix — sortable, globally unique, good index performance. ULID: 26-char Base32, timestamp-sortable, URL-safe. For most PHP apps: BIGINT auto-increment for internal tables, UUID v7 or ULID for externally visible IDs (APIs, URLs). MySQL uses utf8mb4 for UUID storage; PostgreSQL has a native uuid type.

Common Misconception

UUID v4 is the best choice for globally unique IDs — UUID v4's randomness causes B-tree index fragmentation and poor INSERT performance at scale; UUID v7 or ULID are better alternatives.

Why It Matters

Switching from auto-increment to UUID v4 on a high-write table can reduce INSERT throughput by 50%+ due to index fragmentation; UUID v7 gives global uniqueness without the performance penalty.

Common Mistakes

  • UUID v4 as primary key on high-write tables — random inserts fragment the B-tree index.
  • Storing UUIDs as VARCHAR(36) instead of BINARY(16) or native uuid type — 3× the storage and slower indexes.
  • Exposing auto-increment IDs in URLs — reveals business data (order count, user count) and enables enumeration.
  • ULIDs not sorted correctly in PHP — use a ULID library that returns lexicographically sortable strings.

Code Examples

✗ Vulnerable
-- UUID v4 as PK — random, index-fragmenting:
CREATE TABLE orders (
    id VARCHAR(36) PRIMARY KEY DEFAULT (UUID()),  -- Random, large, slow
    total DECIMAL(10,2)
);
-- 1M inserts: B-tree constantly reorganised, cache misses everywhere
✓ Fixed
-- UUID v7 (sortable) or ULID:
CREATE TABLE orders (
    id BINARY(16) PRIMARY KEY,  -- UUID v7: timestamp-prefixed, efficient
    total DECIMAL(10,2)
);

-- PHP with ramsey/uuid:
use Ramsey\Uuid\Uuid;
$id = Uuid::uuid7()->toString(); // 01956b3c-...: sortable, globally unique

-- Or ULID:
$id = (new Ulid())->toRfc4122(); // 01J3K2... — timestamp sortable

Added 15 Mar 2026
Edited 12 Jun 2026
Views 51
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
1 ping T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 1 ping S 1 ping M 2 pings T 1 ping W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 1 ping M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Amazonbot 8 SEMrush 5 Scrapy 5 Perplexity 4 Ahrefs 4 Unknown AI 2 Google 2 Majestic 1 Claude 1 Bing 1
crawler 33
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: Medium
⚡ Quick Fix
Use UUIDv7 (time-ordered) or ULID instead of UUIDv4 for primary keys — random UUIDs fragment B-tree indexes causing write performance degradation on large tables
📦 Applies To
PHP 7.0+ web cli queue-worker laravel
🔗 Prerequisites
🔍 Detection Hints
UUIDv4 as primary key on high-write table causing InnoDB index fragmentation; auto-increment exposing record count via URL
Auto-detectable: ✗ No percona-toolkit mysql-slow-query-log
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Low ✗ Manual fix Fix: Medium Context: File Tests: Update


✓ schema.org compliant