Appearance
PostgreSQL UUID 函数详解
概述
UUID(Universally Unique Identifier,通用唯一标识符)是一个 128 位的数字,用于在分布式系统中唯一标识信息。PostgreSQL 提供了内置的 UUID 生成和处理函数,这些函数在现代应用程序中广泛应用于主键生成、会话标识、文档编号等场景。
INFO
UUID 的优势在于不需要中央协调就能保证全局唯一性,特别适合分布式系统和微服务架构。
UUID 生成函数
gen_random_uuid()
语法:
sql
gen_random_uuid() → uuid
功能说明: 此函数返回版本 4(随机)UUID,这是最常用的 UUID 类型,适用于大多数应用程序。版本 4 UUID 基于随机数生成,具有高度的随机性和唯一性。
实际应用示例
示例 1:用户表主键生成
sql
-- 创建用户表,使用 UUID 作为主键
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
username VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
-- 插入数据时自动生成 UUID
INSERT INTO users (username, email)
VALUES ('alice', '[email protected]');
-- 查看生成的结果
SELECT * FROM users;
输出示例:
id | username | email | created_at
-------------------------------------|----------|-------------------- |-------------------------
a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11 | alice | [email protected] | 2024-01-15 10:30:45.123
分析过程:
DEFAULT gen_random_uuid()
确保每次插入新记录时自动生成唯一的 UUID- UUID 格式为 8-4-4-4-12 的十六进制字符串,总长度为 36 个字符
- 使用 UUID 作为主键可以避免在分布式环境中的 ID 冲突问题
示例 2:订单号生成系统
sql
-- 创建订单表
CREATE TABLE orders (
order_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
customer_id INTEGER NOT NULL,
order_number VARCHAR(20) GENERATED ALWAYS AS ('ORD-' || UPPER(SUBSTRING(order_id::TEXT, 1, 8))) STORED,
total_amount DECIMAL(10,2),
status VARCHAR(20) DEFAULT 'pending'
);
-- 插入订单数据
INSERT INTO orders (customer_id, total_amount)
VALUES (1001, 299.99), (1002, 150.50);
-- 查看生成的订单号
SELECT order_id, order_number, customer_id, total_amount, status FROM orders;
输出示例:
order_id | order_number | customer_id | total_amount | status
-------------------------------------|--------------|-------------|--------------|--------
b1e2f3a4-5c6d-7e8f-9a0b-1c2d3e4f5a6b | ORD-B1E2F3A4 | 1001 | 299.99 | pending
c2f3g4h5-6d7e-8f9a-0b1c-2d3e4f5g6h7i | ORD-C2F3G4H5 | 1002 | 150.50 | pending
分析过程:
- 使用 UUID 确保订单 ID 的全局唯一性
- 通过
GENERATED ALWAYS AS
自动生成人类可读的订单编号 - 即使在多个数据库实例间同步数据,也不会出现 ID 冲突
UUID 扩展模块
uuid-ossp 模块
PostgreSQL 的 uuid-ossp
模块提供了额外的函数,实现了其他标准的 UUID 生成算法。
sql
-- 启用 uuid-ossp 扩展
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- 使用不同版本的 UUID 生成函数
SELECT
uuid_generate_v1() AS "版本1-基于时间",
uuid_generate_v1mc() AS "版本1-MAC随机",
uuid_generate_v4() AS "版本4-随机";
各版本 UUID 对比:
版本 | 生成方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
版本 1 | 基于时间+MAC 地址 | 有时序性,可排序 | 可能泄露 MAC 地址 | 需要时序的日志系统 |
版本 4 | 完全随机 | 隐私性好,无信息泄露 | 无时序性 | 通用场景,用户 ID |
UUID 数据提取函数
uuid_extract_timestamp()
语法:
sql
uuid_extract_timestamp(uuid) → timestamp with time zone
功能说明: 从 UUID 版本 1 中提取带时区的时间戳。对于其他版本,此函数返回 null。
实际应用示例
sql
-- 启用 uuid-ossp 扩展以使用版本1 UUID
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- 创建日志表使用版本1 UUID
CREATE TABLE system_logs (
log_id UUID PRIMARY KEY DEFAULT uuid_generate_v1(),
log_level VARCHAR(10),
message TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
-- 插入日志数据
INSERT INTO system_logs (log_level, message) VALUES
('INFO', '用户登录成功'),
('ERROR', '数据库连接失败'),
('WARN', '内存使用率达到80%');
-- 从 UUID 中提取时间戳并与实际创建时间对比
SELECT
log_id,
log_level,
message,
created_at AS "实际创建时间",
uuid_extract_timestamp(log_id) AS "UUID中的时间戳"
FROM system_logs;
输出示例:
log_id | log_level | message | 实际创建时间 | UUID中的时间戳
-------------------------------------|-----------|-------------------|---------------------|--------------------
01ef2a3b-4c5d-11ef-8e6f-0242ac120002 | INFO | 用户登录成功 | 2024-01-15 10:30:45 | 2024-01-15 10:30:45+08
01ef2a3c-4c5d-11ef-8e6f-0242ac120002 | ERROR | 数据库连接失败 | 2024-01-15 10:30:46 | 2024-01-15 10:30:46+08
分析过程:
- 版本 1 UUID 包含时间戳信息,可以用于数据恢复和审计
- 提取的时间戳精度可能与实际创建时间略有差异
- 这个特性在日志系统和事件溯源中特别有用
uuid_extract_version()
语法:
sql
uuid_extract_version(uuid) → smallint
功能说明: 从符合 RFC 4122 标准的 UUID 中提取版本号。对于其他变体,此函数返回 null。
实际应用示例
sql
-- 测试不同来源的 UUID 版本
WITH uuid_samples AS (
SELECT
gen_random_uuid() AS random_uuid,
uuid_generate_v1() AS time_uuid,
uuid_generate_v4() AS random_v4_uuid
)
SELECT
random_uuid,
uuid_extract_version(random_uuid) AS "random_uuid版本",
time_uuid,
uuid_extract_version(time_uuid) AS "time_uuid版本",
random_v4_uuid,
uuid_extract_version(random_v4_uuid) AS "random_v4_uuid版本"
FROM uuid_samples;
输出示例:
random_uuid | random_uuid版本 | time_uuid | time_uuid版本 | random_v4_uuid | random_v4_uuid版本
-------------------------------------|----------------|--------------------------------------|---------------|--------------------------------------|------------------
a1b2c3d4-e5f6-4789-abcd-ef1234567890 | 4 | 01ef2a3b-4c5d-11ef-8e6f-0242ac120002 | 1 | f1e2d3c4-b5a6-4978-9876-543210fedcba | 4
分析过程:
gen_random_uuid()
生成版本 4 UUIDuuid_generate_v1()
生成版本 1 UUID- 版本信息可用于验证 UUID 的生成方式和数据完整性
UUID 比较操作
PostgreSQL 为 UUID 提供了标准的比较操作符,支持排序和索引操作。
比较操作示例
sql
-- 创建测试数据
CREATE TEMP TABLE uuid_test AS
SELECT gen_random_uuid() AS id, 'test_' || generate_series(1,5) AS name;
-- UUID 比较操作
SELECT
id,
name,
id = (SELECT MIN(id) FROM uuid_test) AS "是否为最小UUID",
id > (SELECT MIN(id) FROM uuid_test) AS "是否大于最小UUID"
FROM uuid_test
ORDER BY id;
UUID 支持的操作符:
操作符 | 说明 | 示例 |
---|---|---|
= | 等于 | uuid1 = uuid2 |
<> | 不等于 | uuid1 <> uuid2 |
< | 小于 | uuid1 < uuid2 |
<= | 小于等于 | uuid1 <= uuid2 |
> | 大于 | uuid1 > uuid2 |
>= | 大于等于 | uuid1 >= uuid2 |
性能考量和最佳实践
索引性能
sql
-- UUID 作为主键的性能测试表
CREATE TABLE performance_test (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
data TEXT
);
-- 创建额外索引
CREATE INDEX idx_performance_test_id_hash ON performance_test USING hash (id);
-- 插入测试数据
INSERT INTO performance_test (data)
SELECT 'test_data_' || generate_series(1, 10000);
-- 性能分析
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM performance_test WHERE id = '01ef2a3b-4c5d-11ef-8e6f-0242ac120002';
存储空间对比
sql
-- 不同主键类型的存储空间对比
CREATE TEMP TABLE space_comparison AS
SELECT
pg_column_size(1::INTEGER) AS "整数大小(字节)",
pg_column_size(1::BIGINT) AS "长整数大小(字节)",
pg_column_size(gen_random_uuid()) AS "UUID大小(字节)",
pg_column_size('user_12345'::VARCHAR) AS "字符串大小(字节)";
SELECT * FROM space_comparison;
输出示例:
整数大小(字节) | 长整数大小(字节) | UUID大小(字节) | 字符串大小(字节)
--------------|----------------|---------------|------------------
4 | 8 | 16 | 13
最佳实践建议
TIP
使用建议
- 主键选择:对于分布式系统,推荐使用 UUID 作为主键
- 版本选择:大多数情况下使用版本 4(
gen_random_uuid()
) - 索引策略:UUID 主键建议使用 B-tree 索引(默认)
- 存储优化:考虑使用二进制存储格式以节省空间
WARNING
注意事项
- UUID 比整数主键占用更多存储空间
- 随机 UUID 可能影响 B-tree 索引的插入性能
- 在高并发插入场景下,考虑使用版本 1 UUID 以获得更好的局部性
高级应用场景
分布式主键生成
sql
-- 多节点环境下的用户表设计
CREATE TABLE distributed_users (
user_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
node_id VARCHAR(10) NOT NULL,
username VARCHAR(50) NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
-- 复合索引优化查询
CONSTRAINT unique_username_per_node UNIQUE (node_id, username)
);
-- 插入来自不同节点的用户
INSERT INTO distributed_users (node_id, username) VALUES
('NODE_001', 'alice'),
('NODE_002', 'bob'),
('NODE_001', 'charlie');
-- 查询特定节点的用户
SELECT user_id, node_id, username
FROM distributed_users
WHERE node_id = 'NODE_001'
ORDER BY created_at;
API 令牌生成
sql
-- API 令牌管理系统
CREATE TABLE api_tokens (
token_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id INTEGER NOT NULL,
token_name VARCHAR(100),
expires_at TIMESTAMP,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT NOW(),
-- 索引优化令牌查找
INDEX idx_api_tokens_active (token_id) WHERE is_active = true
);
-- 生成API令牌
INSERT INTO api_tokens (user_id, token_name, expires_at)
VALUES (1001, 'mobile_app_token', NOW() + INTERVAL '30 days');
-- 令牌验证函数
CREATE OR REPLACE FUNCTION validate_token(token UUID)
RETURNS BOOLEAN AS $$
BEGIN
RETURN EXISTS (
SELECT 1 FROM api_tokens
WHERE token_id = token
AND is_active = true
AND (expires_at IS NULL OR expires_at > NOW())
);
END;
$$ LANGUAGE plpgsql;
-- 使用示例
SELECT validate_token('01ef2a3b-4c5d-11ef-8e6f-0242ac120002') AS "令牌是否有效";
数据迁移和转换
从整数 ID 迁移到 UUID
sql
-- 原有表结构(整数ID)
CREATE TABLE old_products (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
price DECIMAL(10,2)
);
-- 新表结构(UUID)
CREATE TABLE new_products (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
legacy_id INTEGER,
name VARCHAR(100),
price DECIMAL(10,2)
);
-- 数据迁移脚本
INSERT INTO new_products (legacy_id, name, price)
SELECT id, name, price FROM old_products;
-- 创建映射表以支持旧系统兼容
CREATE TABLE id_mapping (
legacy_id INTEGER PRIMARY KEY,
new_uuid UUID NOT NULL REFERENCES new_products(id)
);
INSERT INTO id_mapping (legacy_id, new_uuid)
SELECT legacy_id, id FROM new_products;
通过这些详细的示例和说明,您可以充分理解和掌握 PostgreSQL 中 UUID 函数的使用方法,并将其有效应用到实际的数据库设计和开发中。UUID 函数为现代应用程序提供了强大的唯一标识符生成能力,特别适用于分布式系统和微服务架构。