主键应唯一、非空、稳定、简洁,优先选用无业务含义的代理键(如自增id或uuid);必须满足唯一性、非空性、最小性;避免自然键与复合主键;整型最优,uuid需优化存储;禁用时间戳、频繁变更、含前导零或状态字段等陷阱。

主键应唯一、非空、稳定、简洁,优先选用无业务含义的代理键(如自增ID或UUID),避免使用易变、冗长或含业务逻辑的自然键。
主键必须满足基础约束
主键字段必须满足三个硬性要求:唯一性(UNIQUE)、非空性(NOT NULL)、最小性(不包含多余列)。复合主键虽可行,但会增加关联复杂度和索引体积,一般不推荐。若用多列组合做主键,需确保所有列在业务生命周期内都稳定不变——例如“订单号+商品编码”看似合理,但一旦订单拆分或商品信息变更,就可能破坏主键语义。
优先选择代理主键而非自然主键
自然主键(如身份证号、邮箱、手机号)虽有业务意义,但存在明显风险:
- 身份证号可能重号或格式变更(如15位升18位)
- 邮箱和手机号可被用户修改,违反主键稳定性原则
- 字符串类型主键导致索引更大、JOIN更慢、缓存效率更低
推荐使用数据库内置机制生成代理键:MySQL用AUTO_INCREMENT整型,PostgreSQL用SERIAL或IDENTITY,分布式场景可考虑ULID或雪花ID(Snowflake),兼顾有序性与全局唯一。
主键类型与性能影响
主键即聚簇索引(InnoDB默认),直接影响数据物理存储顺序和查询效率:
- 整型(INT/BIGINT):最高效,比较快、占用空间小(4/8字节)、缓存友好
- UUID(CHAR(36)):全局唯一但写入随机,易造成页分裂、插入慢、索引碎片高;可用UUID_TO_BIN()压缩为BINARY(16),并倒序处理提升局部性
- 字符串主键:除非极特殊场景(如国家代码ISO 2-letter),否则应避免;长度越长,二级索引的“回表”开销越大
避免主键设计常见陷阱
以下做法看似方便,实则埋下隐患:
- 用时间戳(如UNIX秒数)作主键:并发高时易冲突,且不具备唯一性保障
- 在已有主键表上频繁ALTER TABLE ... DROP PRIMARY KEY ADD PRIMARY KEY(...):触发全表重建,锁表时间长
- 主键含前导零或大小写混合(如'001','Abc'):隐式类型转换或排序异常,增加应用层校验负担
- 把状态字段(如is_deleted)加入联合主键:违背主键不可变原则,也使外键引用失效










