UUID v7 The New Default.

The inevitable evolution of primary keys.

Mateus Bosquetti
Mateus Bosquetti
5 min read

Generally, when we create or plan a database table, we start with the ID. It seems like a simple choice, but in reality, a wrong decision about the primary key can cause several headaches over time.

The Sequential Start

The most traditional and widely known type is AUTO_INCREMENT (or BIGINT / BIGSERIAL). It has been taught for decades in universities and courses because it is extremely intuitive and works natively.

Its technical advantages are indisputable for traditional relational databases. It occupies only 8 bytes, guaranteeing high disk performance and efficient B-Tree structures in MySQL and PostgreSQL. Queries and joins (JOIN) are also very fast, as the processor executes integer comparisons natively in memory with maximum cache efficiency.

Now imagine accessing your order on an e-commerce platform and seeing the following URL:

minhaloja.com/pedidos/1004

By exposing this sequential key, anyone can instantly figure out the exact number of company transactions, even allowing bots to scrape the entire database by iterating through the sequence.

Another critical problem is distributed coupling, where microservices or offline systems cannot generate IDs without first consulting the central database, making database writes a mandatory bottleneck.

The Distributed Era

As applications evolved from monoliths to distributed architectures and microservices, AUTO_INCREMENT became a major bottleneck. To solve centralized coupling and metric leaks in URLs, the industry migrated en masse to UUID v4.

The key advantage was enabling decentralized generation, where any microservice creates universal keys without consulting the central database. Because it is virtually 100% random and based on cryptographic entropy, it guarantees absolute secrecy, making it impossible to guess the next key or extract any metadata from the ID.

-
-
-
-
Aleatoriedade Pura (122-bit) v4 (Fixo '4') Variante (8, 9, a, b)

The problem is that UUID v4 performs very poorly in relational databases.

Because it is completely random, inserting a UUID v4 degrades the B-Tree index. The database attempts to write records to random disk pages, causing constant Page Splits (physical page divisions), which leads to high fragmentation and excessive disk I/O.

See in the simulation below how the randomness of UUID v4 fragmentates the database structure with every insertion.

B-Tree Index Simulator

Simulação interativa de alocação de páginas de disco e page splits em índices B-Tree.

Páginas de Disco
0
Page Splits
0
Fragmentação de Disco
0%
Nenhum dado gravado. Clique em "Inserir Lote" para simular escritas.

SQL CONSOLE

READONLY

UUID v7

After researching in search of the perfect key, UUID v7 was the one that caught my attention the most. It delivers the best of both worlds by combining the universal uniqueness of UUID v4 with time ordering (ordered by time) using timestamps.

It encodes the current date and time in its first 48 bits, followed by version and variant bits, and 74 bits of random entropy. In practice, chronologically generated IDs always stay in sequential lexicographical order.

-
-
-
-
Timestamp (48-bit) v7 (Fixo '7') Sub-ms Counter (12-bit) Variante Entropia (62-bit)

This temporal ordering resolves the performance issues in the database. As the initial bits progress linearly over time, each newly inserted key is naturally larger than the previous one. The database writes new records at the end of the B-Tree index in an orderly and continuous manner, eliminating chaotic page splits.

To visualize this gain in practice, you can compare the smooth and sequential writing of UUID v7 against the chaotic fragmentation of UUID v4 directly in the B-Tree index simulator below.

B-Tree Index Simulator

Simulação interativa de alocação de páginas de disco e page splits em índices B-Tree.

Páginas de Disco
0
Page Splits
0
Fragmentação de Disco
0%
Nenhum dado gravado. Clique em "Inserir Lote" para simular escritas.

SQL CONSOLE

READONLY

See below the table declaration using UUID v7 in PostgreSQL 18+ (native support) and MySQL (InnoDB with type BINARY(16)):

SQL SCRIPT

readonly
-- PostgreSQL 18+ (Suporte nativo)
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT uuidv7()
);

-- MySQL (InnoDB)
CREATE TABLE users (
id BINARY(16) PRIMARY KEY
);

However, no architectural choice comes without trade-offs. UUID v7 occupies 16 bytes, double that of a BIGINT (8 bytes). In tables with billions of rows in MySQL (InnoDB), this size propagates across all foreign keys and secondary indexes, requiring more RAM to keep the cache warm.

There is also the privacy issue, as the first 48 bits of UUID v7 record the timestamp in milliseconds, and any key exposed in a public URL carries the moment of its creation. Hover over the highlighted section in the URL below to see the decoded date.

socialnetwork.com/profiles/
0190b4d2-f125
-7b5a-93a8-429be9752f40

In practice, any user or bot can decode this initial segment and discover the exact moment, with millisecond precision, when the record was created in the system.

Other Approaches

Besides traditional UUIDs, there are two derived alternatives created to solve very specific high-performance and interoperability scenarios: ULID and Snowflake ID.

ULID (Universally Unique Lexicographically Sortable Identifier) was designed to be a visually more friendly alternative to UUID. Composed of 128 bits, it is encoded in 26 Base32 characters (Crockford) (e.g. 01ARZ3NDEKTSV4RRFFQ69G5FAV). It removes ambiguous characters (such as I, L, O, 0), making it perfect for REST API routes. However, if the database does not have native support for the ULID type, it ends up being saved as plain text (VARCHAR), losing spatial efficiency.

Timestamp Base32 (10 chars / 48-bit) Entropia Base32 (16 chars / 80-bit)

On the other hand, Snowflake IDs (originally architected by Twitter) combine the compactness of an integer of just 8 bytes (64 bits) with distributed capability. They encode the timestamp, machine identification (Worker ID), and a local counter, supporting massive ingestion rates per second without collisions. The major trade-off is operational complexity: they require external orchestration infrastructure (such as Apache ZooKeeper) to manage Worker IDs and prevent collisions between nodes.

Número Decimal em Disco (64 bits / BIGINT)
1541815603606036480

Na Base 10 não é possível separar os caracteres por texto. A verdadeira fragmentação do Snowflake ocorre a nível binário:

0000000000000000000000000000000000000000000000000000000000000000
Bit 1: Sinal (0) Bits 2-42: Timestamp (41 bits) Bits 43-52: Worker ID (10 bits) Bits 53-64: Contador (12 bits)

Which Key to Choose?

For POCs or simple monolithic projects, AUTO_INCREMENT remains advantageous due to its extreme simplicity and 8-byte efficiency.

But when we talk about professionalism, microservices, and scalability, UUID v7 solidifies itself as the non-negotiable gold standard. It resolves the dilemma between decentralized generation and B-Tree performance. Leave ULID for APIs requiring short text IDs, Snowflake for massive event ingestion, and discard UUID v4 completely in new applications due to severe index degradation and physical fragmentation.

In this article, I chose to focus the analysis exclusively on artificial and independent primary keys, setting aside composite or natural keys as they have their own modeling dynamics.