PostgreSQL中INSERT时可用gen_random_uuid()函数直接生成UUID主键,需先执行CREATE EXTENSION IF NOT EXISTS "pgcrypto";该函数返回标准uuid类型(如1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p),每行独立调用,无需拼接或担心重复。

INSERT 时直接用数据库函数生成 UUID
多数主流数据库内置 UUID 生成函数,无需应用层拼接字符串,也避免了客户端时钟/网络等问题导致的重复或格式错误。关键是选对函数名,并注意其返回值是否带短横线、大小写、是否为二进制。
-
PostgreSQL用gen_random_uuid()(需先CREATE EXTENSION IF NOT EXISTS "pgcrypto"),返回uuid类型,标准格式如1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p -
MySQL 8.0+用UUID(),返回VARCHAR(36),始终小写带短横线;若要存为BINARY(16),得配合UNHEX(REPLACE(UUID(), '-', '')) -
SQL Server用NEWID()(随机)或NEWSEQUENTIALID()(顺序,更适合索引),返回uniqueidentifier类型 -
SQLite没原生 UUID 函数,得靠应用层生成后传入,或用扩展(不推荐生产环境依赖)
批量 INSERT 多行时 UUID 必须逐行独立生成
常见错误是误以为 UUID() 在一条 INSERT ... VALUES (...), (...) 里只调用一次——实际上每行都会重新求值,这是好事;但若写成变量赋值再复用(比如 MySQL 的 @uuid := UUID()),就会所有行得到同一个值,彻底破坏唯一性。
- ✅ 正确(MySQL 示例):
INSERT INTO users (id, name) VALUES (UUID(), 'Alice'), (UUID(), 'Bob'); - ❌ 错误(所有 id 相同):
SET @u = UUID(); INSERT INTO users (id, name) VALUES (@u, 'Alice'), (@u, 'Bob'); - ⚠️ 注意 PostgreSQL 中
gen_random_uuid()在同一语句中多次调用仍保证独立性,无需额外处理
用 UUID 作主键时 INSERT 性能与索引影响
UUID 字符串长度大、无序,直接当主键可能拖慢大批量 INSERT,尤其在 B-tree 索引下频繁页分裂。不是不能用,但得清楚代价。
- MySQL 中若用
CHAR(36)存 UUID,默认排序规则会放大比较开销;改用BINARY(16)+UNHEX(REPLACE(...))能省一半存储并提速索引查找 - PostgreSQL 的
uuid类型本身优化良好,但若表超千万行且写入密集,仍建议搭配pg_partman分区或考虑time-based UUIDv7(需自定义函数) - 别为了“看起来简洁”把 UUID 当
DEFAULT写在表定义里却忘了加NOT NULL——某些驱动或 ORM 在批量插入空值时可能绕过默认值逻辑,导致报错NULL value in column "id" violates not-null constraint
应用层生成 UUID 后传入 SQL 的注意事项
当必须由代码生成(比如跨库一致性、审计要求、或使用 UUIDv4/v7 等定制版本),重点不是“怎么生成”,而是“怎么安全传入”。
- 务必使用参数化查询,绝不要字符串拼接——
INSERT INTO t(id) VALUES ('" + uuid + "')是 SQL 注入温床 - 确认数据库字段类型和长度:PostgreSQL
uuid类型严格校验格式;MySQLCHAR(36)会存下非法字符而不报错,后期查不到数据才暴露问题 - Go/Python/Java 等语言生成的 UUID 默认带短横线,但有些嵌入式数据库或旧版驱动可能期望无横线格式(如
1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p),传之前先统一replace('-', '')

















