文章
关于数据库的表的唯一约束
目录
一、什么是唯一约束(Unique Constraint)#
- 定义 在数据库表中保证某列(或若干列组合)值全局唯一的逻辑层面约束。
- 作用
- 保证数据完整性,防止重复记录
- 自动为受约束列建立唯一索引,加速精确查找
- 可被外键引用,保障引用完整性
- 标准语法示例
CREATE TABLE example ( id INT PRIMARY KEY, email VARCHAR(255) NOT NULL, username VARCHAR(50) NOT NULL, CONSTRAINT uq_user_email UNIQUE (email), UNIQUE (username) ); ```#
二、何时需要使用唯一约束#
- 天然唯一的业务字段
如:
email、手机号、身份证号 - 组合唯一
如:
(order_no, vendor_id)、(title, platform) - 避免重复写入
幂等回调
callback_id、日志(event_id, source)去重 - 外键引用目标 外键只能指向主键或唯一约束列
原则:凡是“逻辑上不允许重复”的字段或字段组合,都应定义唯一约束。
三、优缺点对比#
| 优点 | 缺点/开销 |
|---|---|
| ✅ 数据完整性:自动防止重复,减少脏数据 | ⚠️ 写入延迟:每次 INSERT/UPDATE/DELETE 需更新索引并做冲突检测 |
| ✅ 简化业务逻辑:无需在应用层再重复校验 | ⚠️ 修改成本:约束上线后再改比较困难,需要重建索引或表结构 |
| ✅ 自动索引:受约束列可直接用于查询优化 | ⚠️ 存储空间:索引结构占用额外磁盘和内存 |
| ✅ 便于外键关联:被外键引用时必须唯一 |
四、性能与开销#
- 写入(INSERT/UPDATE/DELETE)
- 索引维护:每次写操作都需维护 B‑Tree(或等价结构)索引
- 冲突检测:插入前需先查索引确认是否冲突
- 查询(SELECT)
- 精准查找:唯一索引可直接定位单行,性能极佳
- 范围扫描:仍能利用索引做区间查询
- 存储空间
- 索引占用额外磁盘文件和内存缓存,大表中索引越多,开销越大
经验:在典型 OLTP 场景中,为关键字段增加 1–2 个唯一约束,写入性能开销通常可以忽略,而带来的数据安全和查询效率提升更为显著。
五、常见使用场景#
| 场景 | 示例字段/组合 | 说明 |
|---|---|---|
| 用户注册 | email、username | 保证每个邮箱/用户名唯一 |
| 商品 SKU 管理 | sku | 确保每个商品的 SKU 唯一 |
| 订单系统 | (order_no, vendor_id) | 同一商家下订单号唯一 |
| 第三方接口回调幂等 | callback_id | 防止重复处理同一回调消息 |
| 日志或事件去重 | (event_id, source) | 确保同一事件不被重复记录 |
| 外键关联 | 被引用列如 user_id | 被其他表引用时必须唯一 |
六、唯一约束 vs. 唯一索引#
虽然在大多数数据库中“唯一约束”都会借助“唯一索引”来实现,但两者在概念和使用上有细微差别:
| 特性 | 唯一约束(Unique Constraint) | 唯一索引(Unique Index) |
|---|---|---|
| 本质 | 逻辑层面的数据完整性规则 | 物理层面的性能优化结构 |
| SQL 标准 | ✅(ALTER TABLE … ADD CONSTRAINT) | ❌(各 DB 实现不统一,需 CREATE UNIQUE INDEX) |
| 自动创建索引 | 创建约束时会自动建立唯一索引 | 手动创建 |
| 外键引用 | ✅(可被外键引用) | ❌(一般不被外键引用) |
| 启用/禁用 | 视数据库而定(如 Oracle 支持 DEFERRABLE/INITIALLY) | ❌(索引通常不可临时禁用) |
| 用途 | 保证逻辑唯一性 | 保证唯一并加速查询 |
| 支持表达式/函数索引 | ❌ | ✅(部分 DB 支持,如 PostgreSQL 表达式索引) |
七、选用建议#
- 需要保证业务逻辑层面唯一(并可能被外键引用)→ 使用唯一约束
- 仅为查询加速或实现去重(无需外键引用)→ 使用唯一索引
- 常见场景:声明唯一约束通常即可兼顾完整性与性能,无需额外手动创建索引
总结: - 唯一约束是数据库数据完整性的首选手段,后台自动创建索引,简化应用逻辑; - 唯一索引更偏向于物理层面的查询优化与去重场景; - 根据实际业务需求选择合适方案,平衡写入性能与数据一致性。