在关系型数据库表结构设计中,主键(PRIMARY KEY)和唯一约束(UNIQUE CONSTRAINT)是最基础也最容易误用的两种限制。主键用于唯一确定一行记录,并且不允许为空;唯一约束则保证某一列或几列的组合在表中不重复,但允许空值存在。理解它们的底层机制与适用边界,是写出可维护、易扩展的SQL schema的前提。

一、主键的设计原则与常见方案
主键的本质是行的唯一身份标识,数据库通常会基于主键构建聚簇索引(如MySQL InnoDB),这意味着数据行物理上按主键顺序存储。因此主键应当尽量短小、有序、不变。如果使用随机字符串或无序业务字段做主键,会引发大量的页分裂与磁盘碎片,严重拖累写入性能。
自增整数(AUTO_INCREMENT)是最简单的主键方案,插入性能高、占用空间小,但在分库分表或数据合并时可能产生冲突。另一种主流做法是使用雪花算法(Snowflake)生成分布式ID,它结合时间戳、机器ID和序列号,保证全局唯一且趋势递增。业务字段如邮箱、身份证号则不适合直接当主键,因为业务规则变化会导致主键变更,而主键变更会连锁影响所有外键引用。
-- 使用自增主键 CREATE TABLE user ( id INT AUTO_INCREMENT PRIMARY KEY, email VARCHAR(100) NOT NULL, name VARCHAR(50) ); -- 使用雪花ID(bigint存储) CREATE TABLE order_info ( id BIGINT PRIMARY KEY, user_id INT NOT NULL, amount DECIMAL(10,2) );
主键选择的优缺点对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| 自增整数 | 写入快、占用小、易排序 | 分布式下易冲突、可被遍历推测数据量 |
| 雪花ID | 全局唯一、适合分库分表 | 依赖时钟、字段较长 |
| 业务字段 | 查询直观 | 易变更、长度大、破坏聚簇性能 |
二、唯一约束的业务语义与用法
唯一约束解决的问题是“某个业务维度不能重复”,例如一个用户只能绑定一个手机号,或者商品编码不能重复。它和主键最大的不同在于:一张表只能有一个主键,但可以有多个唯一约束;唯一约束的列允许NULL,且多个NULL不视为重复(在多数数据库如MySQL、PostgreSQL中)。
在设计时,应将业务唯一性需求显式声明为唯一约束或唯一索引,而不是靠应用层校验。数据库层的约束能防止并发写入导致的脏数据。对于联合唯一,比如“一个用户在同一个群里只能有一条记录”,可以建立多列唯一约束,并注意列的顺序会影响索引的命中效率。
-- 单列唯一约束 CREATE TABLE account ( id INT PRIMARY KEY, phone VARCHAR(20) UNIQUE, email VARCHAR(100) UNIQUE ); -- 联合唯一约束 CREATE TABLE group_member ( user_id INT, group_id INT, joined_at DATETIME, PRIMARY KEY (user_id, group_id), UNIQUE (group_id, user_id) );
唯一约束与唯一索引的差异
在MySQL中,UNIQUE CONSTRAINT底层就是通过唯一索引实现的,但约束更强调语义:它是表结构规则的一部分。使用ALTER TABLE添加的约束在数据库元数据里可被工具识别为业务规则,而单纯建索引可能只是性能考量。建议优先使用CONSTRAINT语法命名约束,方便后期删除与维护。
ALTER TABLE account ADD CONSTRAINT uk_account_email UNIQUE (email);
三、主键与唯一约束协同设计实践
实际项目中,推荐采用“代理主键+业务唯一约束”的模式。代理主键即与业务无关的id,业务唯一性交给唯一约束。这样当业务规则调整,例如允许邮箱可修改,只需变更唯一约束而无需动主键及外键。
还需注意空值处理。如果某业务字段可能暂时为空,但又希望未来填充后不重复,不能用主键承载,只能用唯一约束并明确NULL不参与重复判断。另外在分表场景下,若用雪花ID做主键,仍可在分片内对业务字段建唯一索引,但全局唯一需借助分布式锁或中心化发号器辅助。
-- 推荐实践:代理主键与业务唯一分离 CREATE TABLE product ( id BIGINT PRIMARY KEY, sku_code VARCHAR(30) NOT NULL, CONSTRAINT uk_product_sku UNIQUE (sku_code) );
四、常见误区与避坑建议
一个典型误区是把UUID字符串直接当主键且不加处理。UUID无序且过长,作为InnoDB主键会让聚簇索引膨胀并降低插入效率。若必须用UUID,可转为二进制(16)存储或采用有序UUID变体。
另一个误区是滥用联合主键替代唯一约束。联合主键会让所有外键都必须携带多列,增加关联复杂度。如果多列只是为保证不重复而非标识行,应改设普通主键加联合唯一约束,外键只需引用单列主键即可。
总结:主键负责“找得到这行”,唯一约束负责“业务上不撞车”。二者职责分离,系统才能既稳又灵活。
SQLprimary_keyunique_constraint修改时间:2026-08-04 22:57:34