Skip to main content

PostgreSQL UUID-OSSP Extension

Introduction

When building modern applications, you often need globally unique identifiers for your database records. While serial or bigserial types can provide unique IDs within a single database, they don't guarantee uniqueness across multiple systems. This is where Universally Unique Identifiers (UUIDs) come in.

PostgreSQL's uuid-ossp extension provides functions for generating UUIDs according to several standard algorithms. These UUIDs are 128-bit values designed to be unique across space and time, making them ideal for distributed systems, microservices, and any application where you need to generate IDs without a centralized coordinator.

In this tutorial, you'll learn:

  • What UUIDs are and why they're useful
  • How to enable the uuid-ossp extension
  • Different UUID generation functions
  • Practical use cases and examples
  • Best practices for working with UUIDs

What are UUIDs?

UUID stands for Universally Unique Identifier. It's a 128-bit identifier that follows a standardized format defined by RFC 4122. A UUID looks something like this:

123e4567-e89b-12d3-a456-426614174000

UUIDs have several key advantages:

  • They can be generated without coordination between systems
  • The probability of generating duplicate values is extremely low
  • They don't reveal any information about record creation order (unlike auto-incrementing IDs)
  • They enable easier database sharding and merging

Enabling the UUID-OSSP Extension

Before using UUID functions, you need to enable the extension in your PostgreSQL database. This only needs to be done once per database.

sql
-- Enable the uuid-ossp extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

-- Verify the extension is installed
SELECT * FROM pg_extension WHERE extname = 'uuid_ossp';

Output:

  extname  | extowner | extnamespace | extrelocatable | extversion | extconfig | extcondition
-----------+----------+--------------+----------------+------------+-----------+--------------
uuid_ossp | 10 | 2200 | t | 1.1 | |

UUID Generation Functions

The uuid-ossp extension provides several functions for generating UUIDs using different algorithms:

1. uuid_generate_v1()

Generates a version 1 UUID, which uses the computer's MAC address and current timestamp.

sql
SELECT uuid_generate_v1();

Output:

           uuid_generate_v1
--------------------------------------
a7a9a6f4-c5d2-11ee-a506-3fa06afd8a7b

2. uuid_generate_v4()

Generates a version 4 UUID, which is based on random numbers. This is the most commonly used function.

sql
SELECT uuid_generate_v4();

Output:

           uuid_generate_v4
--------------------------------------
d29a7c8b-1b47-4f3e-8336-d41c66e71d3f

3. uuid_generate_v3() and uuid_generate_v5()

These functions generate "name-based" UUIDs by hashing a namespace identifier and name.

sql
-- Generate a v3 UUID (uses MD5 hash)
SELECT uuid_generate_v3(uuid_ns_url(), 'https://www.example.com');

-- Generate a v5 UUID (uses SHA-1 hash)
SELECT uuid_generate_v5(uuid_ns_url(), 'https://www.example.com');

Output:

           uuid_generate_v3
--------------------------------------
9073926b-929f-31c2-abc9-fad77ae3e8eb

uuid_generate_v5
--------------------------------------
e3fb4a70-d106-5e34-bd5f-4bba825aad24

Namespace Constants

For v3 and v5 UUIDs, the extension provides standard namespace identifiers:

sql
SELECT uuid_ns_dns(); -- DNS namespace
SELECT uuid_ns_url(); -- URL namespace
SELECT uuid_ns_oid(); -- OID namespace
SELECT uuid_ns_x500(); -- X500 namespace

Practical Examples

Example 1: Creating a Table with UUID Primary Key

Using UUIDs as primary keys is a common pattern in modern applications:

sql
-- Create a users table with UUID primary key
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

-- Insert a user with an automatically generated UUID
INSERT INTO users (username, email)
VALUES ('johndoe', '[email protected]')
RETURNING id, username;

Output:

                  id                  | username
--------------------------------------+----------
f47ac10b-58cc-4372-a567-0e02b2c3d479 | johndoe

Example 2: Generating Consistent UUIDs from Names

Version 5 UUIDs are useful when you need to generate the same UUID consistently from the same input:

sql
-- Generate UUIDs for email addresses in a consistent way
SELECT
'[email protected]' AS email,
uuid_generate_v5(uuid_ns_dns(), '[email protected]') AS user_id;

Output:

       email       |                user_id
-------------------+--------------------------------------
[email protected] | e8b764da-5fe5-5d27-9f54-8cca5a4c45b8

This approach ensures that the same email will always map to the same UUID.

Example 3: Creating URL-Safe Identifiers

UUIDs can be used to create secure, random identifiers for URLs:

sql
-- Generate a short, URL-friendly token from a UUID
CREATE OR REPLACE FUNCTION generate_short_id() RETURNS TEXT AS $$
DECLARE
uuid_str TEXT;
base64_str TEXT;
url_safe_str TEXT;
BEGIN
uuid_str := REPLACE(uuid_generate_v4()::TEXT, '-', '');
-- Convert first 16 chars of UUID to a base64-like encoding
base64_str := ENCODE(DECODE(SUBSTRING(uuid_str FROM 1 FOR 16), 'hex'), 'base64');
-- Make it URL safe by replacing + with - and / with _
url_safe_str := REPLACE(REPLACE(base64_str, '+', '-'), '/', '_');
-- Remove padding
RETURN REPLACE(url_safe_str, '=', '');
END;
$$ LANGUAGE plpgsql;

-- Generate some short IDs
SELECT generate_short_id() AS short_id FROM generate_series(1, 3);

Output:

    short_id
----------------
x7d_t3QXdRvLUE
LpFGw1APM_j4JF
kBn_c3FFtx3YAZ

When to Use Each UUID Version

VersionGeneration MethodUse Case
v1MAC address + timestampWhen you need sortable UUIDs or when order matters
v3Namespace + name (MD5)When you need deterministic UUIDs from names (less secure)
v4Random numbersGeneral purpose, most common when uniqueness is the main requirement
v5Namespace + name (SHA-1)When you need deterministic UUIDs from names (more secure than v3)

Performance Considerations

UUIDs have some trade-offs compared to sequential identifiers:

  • They consume more storage (16 bytes vs 4 bytes for integers)
  • They can lead to index fragmentation since they're random
  • They're harder to debug or communicate (not easily memorable)

For high-performance applications, consider these tips:

  • Use uuid as a data type rather than storing them as strings
  • Consider clustering tables by a timestamp if you frequently query by date ranges
  • For very large tables, evaluate if BRIN indexes might be more efficient

Summary

The uuid-ossp extension provides powerful capabilities for generating universally unique identifiers in PostgreSQL. UUIDs solve many challenges in distributed systems and modern application architectures.

Key takeaways:

  • UUIDs ensure global uniqueness without coordination
  • The extension offers multiple UUID generation algorithms
  • Version 4 (random) UUIDs are the most commonly used
  • UUIDs enable easier sharding, merging, and distributed systems
  • They're larger than integer IDs but offer significant advantages in many scenarios

Additional Resources

To learn more about UUIDs in PostgreSQL:

Practice Exercises

  1. Create a table that uses UUIDs for both its primary key and a foreign key relationship.
  2. Write a function that extracts the timestamp from a v1 UUID.
  3. Compare the performance of queries on tables with integer IDs versus UUID IDs.
  4. Implement a system that uses v5 UUIDs to create consistent identifiers for imported data.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)