uuid不适合作为主键,因索引体积大、b+树分裂频繁、mysql 5.7前不支持函数默认值;应改用触发器、应用层生成或mysql 8.0+的uuid_to_bin压缩存储。

MySQL里用UUID()生成唯一ID,但别直接当主键用
直接在表定义里写 DEFAULT UUID() 看似省事,实际会出问题:UUID是字符串(36字符),索引体积大、插入时B+树频繁分裂,性能明显比自增ID差。更麻烦的是,MySQL 5.7及之前不支持函数作为列默认值,连语法都报错——错误信息是 Invalid default value for 'id'。
实操建议:
- 如果必须用UUID,改用触发器或应用层生成,存入
CHAR(36)或压缩成BINARY(16)(用UNHEX(REPLACE(UUID(), '-', ''))) - MySQL 8.0+ 支持
STORED生成列,可搭配UUID_TO_BIN(UUID(), 1)做紧凑存储 - 别把
UUID()塞进PRIMARY KEY,尤其高并发写入场景,热点页争用严重
PostgreSQL的gen_random_uuid()比uuid_generate_v4()更可靠
很多人装完pgcrypto扩展就直接用uuid_generate_v4(),但这个函数依赖系统时间+随机数,若服务器时间回拨或熵池不足,可能产生重复值。而gen_random_uuid()(需pgcrypto)调用的是加密安全的随机源,碰撞概率低到可忽略。
使用前确认:
- 执行
CREATE EXTENSION IF NOT EXISTS pgcrypto;,不是uuid-ossp - 字段类型用
UUID,不是TEXT或CHAR(32),否则索引和比较效率下降 - 建表时写
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,避免应用层拼SQL
SQLite没有原生UUID函数,别硬套random()模拟
有人用hex(randomblob(16))生成32位字符串,看起来像UUID,但本质是伪随机——同一秒内多线程插入可能撞上相同值,且不满足UUID v4格式(缺少版本位)。SQLite本身也不校验格式,后期对接其他系统容易出兼容问题。
更务实的做法:
- 应用层生成标准UUID v4(Python用
uuid.uuid4(),Node用crypto.randomUUID()),再插入 - 如果必须纯SQL,用
printf('%08x-%04x-%04x-%04x-%012x', abs(random()), abs(random()), abs(random()), abs(random()), abs(random())),但仅限测试环境 - 注意:SQLite的
random()不是密码学安全的,生产环境别依赖它防冲突
序列号要“有序+唯一”,优先考虑数据库原生SEQUENCE而非UUID
很多业务其实不需要全局唯一,只要单库内递增、不跳号、能预测下一条——比如订单号、工单号。这时候UUID反而是累赘:难读、难调试、无法分页排序。
各数据库的推荐路径:
- PostgreSQL:用
CREATE SEQUENCE order_seq START 100000;,插入时NEXTVAL('order_seq') - MySQL 8.0+:用
CREATE TABLE orders (id BIGINT AUTO_INCREMENT, ...),配合auto_increment_offset做分库分段 - SQL Server:
CREATE SEQUENCE order_seq AS BIGINT START WITH 100000 INCREMENT BY 1;
真正需要跨库/跨服务唯一时,再上UUID;否则先让序列扛住90%的场景——毕竟人眼识别ORD-20240521-100001比550e8400-e29b-41d4-a716-446655440000轻松得多。










