Why Java 26’s New UUID API Fixes the Biggest UUID Pitfall

The article explains how random UUIDs degrade database write performance, introduces the RFC 9562 UUIDv7 format that embeds a timestamp for natural ordering, shows JDK 26’s new UUID.ofEpochMillis() API, discusses its monotonicity limitation, and compares UUIDv7 with Snowflake and ULID for practical ID generation.

Java Companion
Java Companion
Java Companion
Why Java 26’s New UUID API Fixes the Biggest UUID Pitfall

Why random UUIDs hurt write performance

Using UUID.randomUUID() (v4) as a primary key creates a completely random 128‑bit value. Each insert must locate a random position in the B‑tree index, resulting in very low cache‑hit rates, frequent page splits, and heavy random I/O. The problem appears in relational databases (MySQL InnoDB, PostgreSQL B‑tree), log/event stores, and distributed tracing where ordering by ID becomes impossible.

UUID versions in Java

Java provides two factories: UUID.randomUUID() (v4, pure random) and UUID.nameUUIDFromBytes() (v3, name‑based hash). v4 is unsortable because the 128 bits have no temporal correlation.

RFC 9562 UUIDv7 – embedding time

RFC 9562 (May 2024) defines several new UUID versions; v7 is the most notable. Its layout is:

|<------------------- 128 bits UUID ------------------->|
|   48 bits   | 4 | 12 bits | 2 |      62 bits      |
|Unix ms ts  |v| rand_a  |var|      rand_b       |

First 48 bits : Unix timestamp in milliseconds (covers up to year 10889).

Next 4 bits : version = 7.

Middle 12 bits (rand_a) : optional monotonic counter suggested for high‑concurrency.

Last 62 bits (rand_b) : high‑quality random bits for global uniqueness.

IDs generated in the same millisecond share the same high‑order timestamp, so their lexical order matches creation time, and B‑tree inserts become sequential, eliminating random I/O.

JDK 26 native support

JDK 26 adds a static factory method:

public static UUID ofEpochMillis(long timestamp)

Passing System.currentTimeMillis() returns a UUIDv7 where the timestamp occupies the high bits. Example:

UUID uuid = UUID.ofEpochMillis(System.currentTimeMillis());
System.out.println(uuid);
// 0196c4e3-f8a1-7b2d-a3f0-1c9e4d7b3e2a  (timestamp encoded in the prefix)

Ordering can be verified:

UUID a = UUID.ofEpochMillis(System.currentTimeMillis());
Thread.sleep(10);
UUID b = UUID.ofEpochMillis(System.currentTimeMillis());
System.out.println(a.compareTo(b)); // negative, a < b

The compareTo implementation compares the 128‑bit integer directly, so the timestamp determines order.

Monotonicity caveat under high concurrency

RFC 9562 requires UUIDv7 to be monotonically increasing on a single node, but JDK 26’s implementation fills the 12‑bit rand_a with a random value, not a counter. Consequently, two IDs generated within the same millisecond can be out of order:

long now = System.currentTimeMillis();
UUID a = UUID.ofEpochMillis(now);
UUID b = UUID.ofEpochMillis(now);
System.out.println(a.compareTo(b)); // may be positive → out‑of‑order

For most CRUD workloads this occasional inversion is negligible, but systems that rely on strict monotonic IDs (financial ledgers, audit logs, pagination via WHERE id > last_id) need a custom solution. One approach is to maintain an atomic timestamp that always increases:

private static final AtomicLong lastTimestamp = new AtomicLong(0);
public static UUID generateMonotonicUUIDv7() {
    long now = System.currentTimeMillis();
    long ts = lastTimestamp.updateAndGet(prev -> Math.max(prev + 1, now));
    return UUID.ofEpochMillis(ts);
}

This guarantees ordering at the cost of a few milliseconds of timestamp drift under extreme load.

Comparison with Snowflake and ULID

Snowflake ID (Twitter, 2010) is a 64‑bit integer (41‑bit timestamp, 10‑bit machine ID, 12‑bit sequence). It uses half the storage of a UUID and is naturally monotonic, but requires a central coordinator or pre‑allocated machine IDs and caps at 4096 IDs per millisecond.

ULID (Universally Unique Lexicographically Sortable Identifier) is 128‑bit, composed of a 48‑bit timestamp and 80‑bit random part, encoded in Crockford Base32. It offers similar time precision to UUIDv7 and shorter, more readable strings, but lacks an official RFC and has inconsistent language implementations.

UUIDv7 benefits from being an official RFC, needing no external dependencies, and being supported natively in PostgreSQL 17 and upcoming MySQL 9.x. With JDK 26 you can generate it without third‑party libraries.

Choosing a scheme:

If you are in a pure Java stack, do not need extreme storage efficiency, and your throughput is well below a few thousand IDs per millisecond, UUIDv7 is the simplest choice.

For high‑throughput financial systems where 8‑byte integer keys and guaranteed monotonicity matter, Snowflake remains preferable.

ULID’s niche is shrinking as UUIDv7 gains adoption.

Adopting UUIDv7 in existing projects

For JDK 17/21 you can use the com.fasterxml.uuid:java-uuid-generator library, which already supports UUIDv7:

// java-uuid-generator
UuidCreator.getTimeOrderedEpoch();

After upgrading to JDK 26, replace the library call with the native UUID.ofEpochMillis() – the change is almost seamless.

In new projects, configure JPA/Hibernate primary keys as UUID and set the generation strategy to UUIDv7; Spring Boot 3.x and Hibernate 6.x have built‑in support.

For legacy data, migrate carefully: run a transitional period where both old and new IDs coexist, update foreign‑key relationships at the application layer, and batch‑update primary keys only after a full backup.

Note the privacy implication: the first 48 bits expose the creation timestamp, which may be undesirable for IDs exposed publicly (e.g., in URLs).

Overall, after two decades of random‑UUID performance pain, JDK 26 finally offers a zero‑dependency, standards‑based solution that can improve write throughput without sacrificing uniqueness.

JavaUUIDSnowflakedatabase indexingidentifier generationULIDUUIDv7JDK 26
Java Companion
Written by

Java Companion

A highly professional Java public account

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.