POSTGRESQL

UUIDv7 in PostgreSQL 18: What You Need to Know

intro

Let’s learn everything you need to know about the new UUIDv7 features in PostgreSQL 18.

Tools used in the tutorial
Tool Description Link
Dbvisualizer DBVISUALIZER
TOP RATED DATABASE MANAGEMENT TOOL AND SQL CLIENT

UUIDs represent a popular way to handle unique identifiers, but version 4 (the latest one previously supported by PostgreSQL) has notable limitations, including poor indexing performance and the inability to produce meaningful sorting. UUIDv7 addresses both issues by being time-ordered, which improves index efficiency and enables chronological sorting.

In this article, you will learn everything you need to know about UUIDv7 support in PostgreSQL 18. Discover how it brings a breath of fresh air to ID management!

Understanding the Role of UUIDs in Databases

Relational databases have traditionally used auto-incrementing data types (such as SERIAL, in Postgres) to generate unique IDs. This mechanism works well on a single machine, but when scaling across multiple nodes, you need a way to generate identifiers that are unique within your application or global environment. That is where UUIDs come in!

According to RFC 4122, a UUID (short for Universally Unique Identifier) is a 128-bit, fixed-size value. In detail, UUIDs are generally used to uniquely identify information in computer systems. In databases like PostgreSQL, UUIDs can serve as primary keys instead of traditional auto-incrementing values.

The main advantage of UUIDs is that they are designed to be unique across both space and time. Plus, they can be generated at high throughput without requiring a centralized authority. The likelihood of two UUIDs being identical is extremely low (approximately 1 in 2¹²², which is about 1 in 5.3×10³⁶).

Transitioning from SERIAL IDs to UUIDs is a common practice in distributed PostgreSQL deployments. For example, the Instagram engineering team adopted UUIDs when they sharded their PostgreSQL database to ensure global uniqueness across multiple nodes. Learn more in our guide on distributing data via database sharding.

Why Use UUIDs as Primary Keys in Databases?

UUIDs are a strong alternative to auto-incrementing IDs as primary keys for these reasons:

  • In distributed or sharded databases, generating sequential IDs requires coordination. UUIDs remove this dependency by allowing IDs to be generated independently across nodes. That makes them ideal for scaling databases horizontally across multiple servers.
  • UUIDs can be generated on the client, which is useful in mobile apps, offline-first solutions, or serverless environments. Clients can create records with UUIDs locally, then sync them with the server later without conflicts.
  • Auto-incrementing IDs are predictable. For instance, if a user sees an ID 1021 associated with a customer, they might infer that there are at least 1020 customers and attempt to access their data by using adjacent IDs. Instead, UUIDs are virtually impossible to guess or enumerate. This makes them a safer choice for URLs, APIs, and other public interfaces.
  • When data is created in parallel across different systems or devices, such as in collaborative tools, UUIDs minimize the risk of primary key collisions. Their design is built around achieving global uniqueness, helping you merge datasets from multiple sources.

UUID Support in PostgreSQL Before Version 18

Before PostgreSQL 18, the database already supported UUIDs through the dedicated UUID data type. This type stores a 128-bit value in a compact binary format and is fully compliant with RFC 4122.

In detail, PostgreSQL allows you to generate and work with UUIDs directly in SQL through the following functions:

  • gen_random_uuid(): Generates a version 4 (random) UUID. This is the most widely used UUID variant and suits most general-purpose applications.
  • uuid_extract_timestamp(uuid): Extracts a timestamp with time zone from a version 1 UUID.
  • uuid_extract_version(uuid): Returns the UUID version number (e.g., 1, 4) for UUIDs that follow the RFC 4122 variant.

Additionally, UUIDs can be used natively as primary keys. PostgreSQL supports them efficiently with both B-tree and hash indexes.

The Need for UUIDv7

Over the years, UUIDv4 has become a popular choice for generating globally unique identifiers in PostgreSQL, especially in distributed environments. At the same time, when used as a primary key, it comes with these three serious challenges:

  • Lack of sortability: UUIDv4 values are completely random and cannot be meaningfully sorted by value.
  • Poor index locality: Since UUIDv4 values are random, new entries are scattered across the index. This leads to fragmented B-tree indexes, and they lead to inefficient inserts and degraded performance.
  • Storage and bandwidth overhead: UUIDs are 128-bit values, significantly larger than typical INT (32-bit) or BIGINT (64-bit) keys. While modern CPUs can compare UUIDs efficiently, the larger size may still impact storage and network usage.

To address these challenges, the new UUIDv7 format was introduced in RFC 9562, published in May 2024.

Compared to UUIDv4, UUIDv7 values are time-based. That means the first 48 bits encode the Unix timestamp, while the remaining bits ensure randomness and uniqueness. This structure makes UUIDv7 sortable by creation time and inserts more index-friendly, resolving the two most critical issues of UUIDv4.

AspectUUIDv4UUIDv7
UniquenessGlobally uniqueGlobally unique
Size128 bits128 bits
Generation methodRandom-basedTime-based (Unix timestamp + random)
SortabilityNot sortableSortable by creation time
Index localityPoor (random distribution)Good (sequential insert order)
Performance ImpactCan cause index fragmentationReduced fragmentation, better write performance
Use caseGeneral-purpose unique IDsOptimized for databases and distributed systems requiring sortable keys
Example7f9c3608-b686-47f9-81a0-8366cbe8197001987fa9-59f4-752b-98bf-cdae2f2b5f44

To experiment with UUIDv7 and learn more about its benefits, visit the UUIDv7 site.

UUIDv7 Introduction in PostgreSQL 18

PostgreSQL 18, currently in Beta 2 and expected to be released by the end of the year, introduces the new uuidv7() SQL function. This function generates UUID version 7 identifiers, following the specification defined in RFC 9562.

As explained in the official commit, UUIDv7 combines a Unix timestamp in milliseconds with random bits, providing both global uniqueness and time-based sortability. In PostgreSQL’s implementation, a 12-bit sub-millisecond timestamp fraction is stored immediately after the main timestamp.

That fraction acts as a counter that ensures monotonicity within the same millisecond. This detail helps maintain an increasing order of generated UUIDs even if the system clock moves backward or when many UUIDs are created very quickly in the same backend process. In other words, it guarantees that UUIDv7 values generated by the same backend are strictly increasing.

On top of that, PostgreSQL 18 updates the uuid_extract_timestamp() function to support UUID version 7. For consistency, an alias uuidv4() is also added for the existing gen_random_uuid() function.

PostgreSQL UUIDv7 in Action

See how the new PostgreSQL UUIDv7 mechanism works with some query examples:

Copy
        
1 SELECT uuidv7();

The above function will return a UUIDv7 value, such as:

Copy
        
1 '01987fb8-b258-70fd-a574-1c3f9b89ee21'

You can verify that this is a valid UUIDv7 using tools like UUIDGen Validator:

Note that the UUID is a v7 one
Note that the UUID is a v7 one

The value appears to be a valid UUIDv7 value as expected.

To confirm the version directly in PostgreSQL, use the uuid_extract_version() function:

Copy
        
1 SELECT uuid_extract_version('01987fb8-b258-70fd-a574-1c3f9b89ee21'::uuid);

The result will be:

Copy
        
1 7
Executing the uuid_extract_version function in DbVisualizer
Executing the uuid_extract_version function in DbVisualizer

Similarly, check the result on the new uuidv4() alias:

Copy
        
1 SELECT uuid_extract_version(uuidv4());

You will get, as you could expect, the following result:

Copy
        
1 4

Then, given a UUIDv7, you can also retrieve its timestamp with uuid_extract_timestamp():

Copy
        
1 SELECT uuid_extract_timestamp('01987fb8-b258-70fd-a574-1c3f9b89ee21'::uuid);

The result will be:

Copy
        
1 2025-08-06 14:11:07.480+00

Et voilà! You now know how to generate, inspect, and use UUIDv7 values in PostgreSQL.

Conclusion

In this blog post, you discovered why UUIDs are an indispensable mechanism for PostgreSQL databases, especially in shared or distributed environments. You explored the pros and cons of using UUIDs to identify rows, and saw how PostgreSQL 18 introduced support for the new UUIDv7 format.

Managing PostgreSQL becomes even easier with a feature-rich visual tool like DbVisualizer. It stays up to date with the latest PostgreSQL features and offers powerful capabilities such as query optimization, drag-and-drop query building, ERD-style schema diagrams, and streamlined import/export operations. Download DbVisualizer for free today!

FAQ

Does PostgreSQL support UUIDv7?

Yes, since PostgreSQL 18. Postgres version 18 introduced support for UUIDv7. This new version of UUID is time-ordered, so that it supports scenarios that require both uniqueness and chronological sorting.

What are the main UUID changes in PostgreSQL 18?

The key changes brought by PostgreSQL 18 when it comes to UUIDs are:

  • Introduction of the uuidv7() function to generate time-sortable UUID version 7.
  • Expansion of uuid_extract_timestamp() to support UUIDv7.
  • Addition of uuidv4() alias for the existing gen_random_uuid() function for consistency.

What is the uuidv7 function in PostgreSQL 18?

The PostgreSQL 18 uuidv7() function follows this syntax:

Copy
        
1 uuidv7([shift interval])

It accepts an optional shift parameter that allows you to adjust the generated timestamp by the specified time interval. Then, it generates a UUIDv7 value. The timestamp component of the UUID is based on the Unix epoch time with millisecond precision, combined with sub-millisecond data and random bits for uniqueness.

Is the pgcrypto extension required to call gen_random_uuid()?

No, not anymore. The pgcrypto extension originally provided the gen_random_uuid() function. However, that function has been included in PostgreSQL’s core since version 13. As a result, you no longer need to install or enable pgcrypto to use gen_random_uuid(). The built-in version is now the standard, making the pgcrypto version obsolete (it calls the core function with the same name under the hood). So, you can simply call gen_random_uuid() directly in modern PostgreSQL installations.

Why use a database client?

A visual database client like DbVisualizer simplifies database tasks by making it easy to query, manage, and visualize data. Plus, it stays up to date so that you do not have to worry about missing new features like UUIDv7 in PostgreSQL 18. With a user-friendly interface, it lets you work with tables, explore schema relationships, and craft or debug queries using its powerful built-in SQL editor. Notable features include autocomplete, ER diagrams, and data export tools, helping DbVisualizer stand out. Try the Pro version with a 21-day free trial!

Dbvis download link img
About the author
Antonello Zanini

Antonello is a software engineer, and often refers to himself as a technology bishop. His mission is to spread knowledge through writing.

The Table Icon
Sign up to receive The Table's roundup
More from the table
Title Author Tags Length Published
title

SQL Boolean Type: How to Use It in All Major Relational Databases

author Antonello Zanini tags MySQL ORACLE POSTGRESQL SQL SQL SERVER 8 min 2025-09-23
title

Postgres List Schemas: 3 Different Approaches

author Antonello Zanini tags POSTGRESQL 5 min 2025-09-15
title

pgvectorscale: An Extension for Improved Vector Search in Postgres

author Antonello Zanini tags AI POSTGRESQL Vectors 9 min 2025-09-03
title

PostgreSQL Index Mechanism: A Guide to Database Performance Optimization

author Leslie S. Gyamfi tags POSTGRESQL 8 min 2025-09-02
title

PostgreSQL ISNULL Equivalent: COALESCE, CASE, and NULLIF

author TheTable tags POSTGRESQL 5 min 2025-08-19
title

SQL IS NOT NULL Condition: Definitive Guide

author Antonello Zanini tags MySQL ORACLE POSTGRESQL SQL SQL SERVER 6 min 2025-08-13
title

Can a Primary Key Be NULL in an SQL Table?

author TheTable tags MySQL ORACLE POSTGRESQL SQL SQL SERVER 5 min 2025-08-12
title

ISNULL vs COALESCE: Comparing NULL Handling Functions

author Antonello Zanini tags MySQL ORACLE POSTGRESQL SQL SQL SERVER 6 min 2025-08-06
title

The SELECT INTO TEMP TABLE Mechanism in SQL

author Antonello Zanini tags MySQL ORACLE POSTGRESQL SQL SQL SERVER 6 min 2025-08-05
title

PostgreSQL TRUNCATE TABLE Statement: A Guide

author Leslie S. Gyamfi tags POSTGRESQL 6 min 2025-07-30

The content provided on dbvis.com/thetable, including but not limited to code and examples, is intended for educational and informational purposes only. We do not make any warranties or representations of any kind. Read more here.