Databases 8 min read

Why UUID Primary Keys Slow Down MySQL and How to Optimize Them

This article explains how using UUIDs as primary keys in MySQL can degrade index efficiency, insert and query performance, and cause costly index refreshes on updates, then presents practical techniques such as ordered UUIDs, binary storage, hybrid keys, and partitioning to mitigate these issues.

macrozheng
macrozheng
macrozheng
Why UUID Primary Keys Slow Down MySQL and How to Optimize Them

Problems with UUID Primary Keys

Characteristics of UUID

UUID is a 128‑bit identifier usually rendered as a 36‑character string, e.g. 550e8400-e29b-41d4-a716-446655440000.

Globally unique, which makes it attractive for distributed systems.

Drawbacks of using UUID as the primary key

Index size : Storing a UUID as CHAR(36) consumes 36 bytes per row, whereas a BIGINT primary key uses only 8 bytes. Larger indexes increase storage cost and I/O.

Index splits : UUIDs are random and unordered, so inserts are distributed across the B‑tree. This causes frequent page splits and tree rebalancing, degrading write performance.

Insert performance : Random placement forces InnoDB to modify many leaf pages, leading to higher disk I/O and lower throughput.

Query performance : String comparison is slower than integer comparison, and the larger index range reduces cache efficiency.

Why Updating Data Triggers Index Refreshes

Role of indexes

Indexes (typically B+‑trees) accelerate point lookups and range scans.

When a row is modified, every index that references the changed columns must be updated to keep the index consistent with the table data.

Impact of updates

Primary‑key update : MySQL deletes the old index entry and inserts a new one, which may require tree rotations and additional I/O.

Non‑primary indexed column update : The same delete‑insert cycle occurs for each affected secondary index.

Extra cost for UUID primary keys

Because UUIDs are unordered, changing a UUID often moves the row to a different position in the index, causing more page splits and tree rebalancing than an ordered key such as an auto‑increment integer.

Why Character Primary Keys Reduce Efficiency

Storage overhead

Character keys (e.g., CHAR(36)) occupy more space than numeric keys, inflating the size of the primary‑key index and any secondary indexes that reference it.

Comparison cost

String comparison involves byte‑wise evaluation, which is slower than the single‑CPU‑instruction integer comparison used for BIGINT keys.

Example: WHERE id='550e8400-e29b-41d4-a716-446655440000' is slower than WHERE id=12345.

Index splits

Unordered character keys cause frequent B‑tree page splits, leading to additional I/O and reduced insert throughput.

Optimizing UUID Primary Key Performance

Use ordered UUIDs

Adopt time‑based UUID versions (e.g., UUIDv7) that generate values in roughly increasing order, reducing page splits.

Ordered UUIDs can be built from a timestamp plus a random component, preserving uniqueness while keeping inserts sequential.

Store UUID as binary

Saving the UUID in a BINARY(16) column cuts the storage requirement from 36 bytes to 16 bytes and improves index density.

CREATE TABLE users (
  id BINARY(16) PRIMARY KEY,
  name VARCHAR(255)
);

Combine an auto‑increment key with a UUID column

Use a small, sequential BIGINT as the physical primary key for fast inserts, and keep a separate CHAR(36) (or BINARY(16)) column with a UNIQUE index for logical identification.

CREATE TABLE users (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  uuid CHAR(36) UNIQUE,
  name VARCHAR(255)
);

Partition large tables

Partitioning the table (by range, hash, or list) limits the size of each individual index tree, which can improve both insert and query performance on very large datasets.

Summary

UUID primary keys increase index size, cause random inserts, and make updates expensive because of frequent B‑tree rebalancing.

Character‑based primary keys suffer from the same storage and comparison inefficiencies.

Mitigation strategies include using ordered UUIDs, storing UUIDs as BINARY(16), employing a surrogate auto‑increment key together with a UUID, and partitioning large tables to keep index trees manageable.

PerformanceOptimizationMySQLUUIDIndex
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.