Skip to content
Back to blog

How UUIDv7 Made Time Part of UUID Ordering

4 min read
How UUIDv7 Made Time Part of UUID Ordering

UUID was not invented as a prettier way to write an identifier. It answered a practical distributed-systems problem: different machines needed to create unique values independently, without asking one central service for the next number. That idea survived several platforms and standards, but the most popular version, UUIDv4, left databases with the awkward insertion pattern of a fully random key. UUIDv7 keeps decentralized generation while moving time to the front of the value, which usually places identifiers created near each other close together when they are sorted.

From Apollo NCS to an IETF standard

The ancestor of the modern UUID appeared in Apollo Computer's Network Computing System. Objects in a distributed system needed identifiers that could be issued on different computers without central coordination. The Open Software Foundation later carried this approach into its Distributed Computing Environment, which is why older documentation still uses the name DCE UUID. Microsoft adapted the same general format under the name GUID for COM and Windows.

In 2005, RFC 4122 collected widely used UUID variants in one IETF document. Version 1 combined time, a clock sequence, and a 48-bit node field that could hold a network interface address or a randomized replacement. Version 2 belonged to DCE Security, versions 3 and 5 deterministically derived a value from a namespace and name using MD5 or SHA-1, and UUIDv4 devoted 122 bits to random data. Version 4 became the common choice for public identifiers because it is easy to generate and does not encode a timestamp or device information.

In May 2024, RFC 9562 replaced RFC 4122, clarified the older formats, and standardized versions 6, 7, and 8. UUIDv6 rearranges the fields of UUIDv1 so time comes first, UUIDv8 reserves room for application-specific schemes, and UUIDv7 uses a modern Unix timestamp without inheriting the field layout of v1.

Why a random key makes B-tree work less local

The statement that a database stores rows in primary-key order is not true for every system. PostgreSQL normally keeps rows in a heap table and creates a separate unique B-tree for a PRIMARY KEY. InnoDB, by contrast, uses the primary key as a clustered index that contains the row data. Despite that architectural difference, the index in either system must locate the page for a new key and add an entry there.

UUIDv4 spreads inserts evenly across the full index range. As an index grows, that can enlarge the working set of pages, reduce cache locality, and split full pages in many parts of the tree. UUIDv7 directs values created around the same time into a neighboring B-tree range, so inserts usually touch fewer pages. This is a useful property, not a universal performance guarantee. The result depends on the database engine, index size, fill factor, storage, and workload. Under heavy concurrency, sequential keys can also create contention at the right edge of an index.

An auto-incrementing key is not inherently unsafe or badly designed. It is compact, simple, and often an excellent internal key for a single database. The drawback appears when the sequence is exposed publicly and reveals an approximate record count, or when several nodes must create identifiers without coordinating with one sequence. UUIDv7 is useful in those cases because it combines decentralized generation with a more index-friendly order.

How UUIDv7 is laid out

The first 48 bits of UUIDv7 contain the number of milliseconds since the Unix epoch. They are followed by four version bits, a 12-bit rand_a field, two variant bits, and a 62-bit rand_b field. In a typical implementation, the last two random fields provide 74 random bits in total. The RFC also permits an implementation to use some of that space for a sub-millisecond fraction or a monotonic counter, but that is an optional generation strategy rather than a mandatory part of the format.

Because the timestamp occupies the most significant bits, a UUIDv7 from a later millisecond compares as a larger 128-bit value. This provides a useful approximate chronology, but it does not promise a total ordering of events. If several identifiers are created in one millisecond and the remaining bits are random, their order relative to one another is random as well. A clock moving backward can also break the sequence between milliseconds unless the implementation handles that case explicitly.

Generation support depends on the database version. PostgreSQL 18, for example, added a built-in uuidv7() function and timestamp extraction for UUIDv7. If a database can store UUID values but cannot generate version 7 itself, the application can create the identifier and store it as the same 128-bit type. Check the documentation for the exact database version before relying on a native function.

Exact RFC 9562 UUIDv7 layout with a 48-bit timestamp, version and variant fields, 74 random bits, and a separate optional monotonic scheme

How UUIDv7 differs from ULID, NanoID, and CUID2

The canonical ULID specification also puts a 48-bit Unix millisecond timestamp first, then encodes it with 80 random bits as 26 characters of Crockford Base32. The alphabet and timestamp position make ULID strings lexicographically sortable. Basic random generation does not guarantee order within the same millisecond, although a separate monotonic API can increment the random portion for that purpose.

NanoID and CUID2 address a different problem. They offer compact or hard-to-guess identifiers, but a leading timestamp is not a required property of either format, so they should not be described as direct sortable alternatives to UUIDv7. The right choice depends on whether a project values a standard database UUID type, chronological locality, shorter text, or compatibility with a particular library.

What our generator creates

The UUID Generator creates UUIDv4, UUIDv7, and UUIDv1 with a randomized node instead of a real MAC address. It also offers ULID, NanoID, and a SHA-256-based CUID2-like identifier. The last option follows the broad idea rather than calling the official CUID2 library. The tool uses crypto.getRandomValues for random data, supports batch generation, and can change the textual representation of its output.

Our UUIDv7 implementation fills all 74 available bits with random data. It does not add a same-millisecond counter, so several values generated almost simultaneously are not guaranteed to preserve issue order. To inspect the time component, select UUIDv7, generate a few values with a short pause between them, and compare their leading characters. UUIDv4 will show no equivalent pattern because it contains no timestamp.

Frequently asked questions

Why is UUIDv7 better than UUIDv4?

UUIDv7 puts time in the most significant bits, so identifiers from different milliseconds sort roughly in creation order. When UUIDs are used as indexed keys, that usually gives inserts better locality than fully random UUIDv4 values. UUIDv7 does not guarantee strict ordering within one millisecond unless the generator adds a counter or another monotonic scheme.

Does UUIDv1 always expose the computer's MAC address?

No. In the classic design, the node field could contain an IEEE network address, so some UUIDv1 values did reveal information about their source device. The specification also permits a random or pseudorandom node with the multicast bit set. Our generator uses a randomized node and does not read the computer's MAC address.

Are ULID and UUIDv7 the same format?

No, although both formats begin with a 48-bit Unix millisecond timestamp. ULID uses 26 characters of Crockford Base32 and keeps 80 bits for randomness, while UUIDv7 preserves the standard 128-bit UUID layout with version and variant fields. Both need a monotonic implementation if strict ordering within one millisecond is required.

Can two random UUIDs collide?

A collision is theoretically possible. UUIDv4 has 122 random bits, and the birthday bound puts the probability of at least one collision near 50% after roughly 2.7×10^18 values. That does not remove the need for a sound random number generator, but it makes collisions negligible at ordinary application volumes.

Author

Fullstack Engineer

In commercial development since 2011 — over 15 years. Author of two SaaS products, TeleGo.io and LingvoHabit.com. The articles here come out of real client and product work.

About the authorWrite on Telegram

Related Articles