两者解决的不是同一件事
唯一索引承担业务约束(值不允许重复),并让等值查找一旦命中就能停止;普通索引只负责支撑查询与排序,允许重复值。选型时先问「这个唯一性是不是业务硬要求」,再考虑查询性能——为了提速把普通索引改成唯一索引,通常是本末倒置。
基本信息
| 唯一索引用途 | 业务约束加等值查找 |
|---|---|
| 普通索引用途 | 支撑查询与排序,允许重复值 |
| 查找差异 | 唯一索引命中即可停止;普通索引还要继续检视重复值 |
| 写入成本 | 唯一索引插入要做冲突检查,并发冲突需业务处理 |
| NULL 语义 | 两库默认允许多个 NULL;PostgreSQL 15+ 可用 NULLS NOT DISTINCT 改变 |
典型使用场景
- 注册邮箱、手机号必须唯一
- 订单号、流水号不允许重复
- 状态列、外键列只用于查询,建普通索引即可
- 复合唯一键表达「同一用户同一类型只能有一条」
- 用唯一索引的冲突来探测重复写入,配合 upsert 做幂等
示例
示例
-- 业务唯一:注册邮箱不允许重复
ALTER TABLE users ADD UNIQUE KEY uk_email (email);
-- 复合唯一:同一用户同一渠道只能绑定一条
ALTER TABLE bindings ADD UNIQUE KEY uk_user_channel (user_id, channel);
-- 仅查询支撑:允许重复值
ALTER TABLE orders ADD INDEX idx_user_created (user_id, created_at);
-- PostgreSQL:唯一约束会创建对应的唯一索引
ALTER TABLE users ADD CONSTRAINT uk_users_email UNIQUE (email);两种索引对照
| 维度 | 唯一索引 | 普通索引 |
|---|---|---|
| 重复值 | 不允许(NULL 除外) | 允许 |
| 等值查找 | 命中即停 | 命中后还要继续检视 |
| 写入成本 | 额外做冲突检查,可能抛唯一冲突 | 只维护索引结构 |
| 与约束关系 | 通常就是约束的载体 | 纯性能对象 |
| 删除影响 | 可能一并移除业务约束 | 只影响性能 |
唯一性由业务决定,不能为了提速把普通索引改成唯一索引:列上本来就有重复值,约束建不起来,语义也不对。
唯一索引的 NULL 语义两库默认一致:多个 NULL 互不冲突。PostgreSQL 15 起可用 NULLS NOT DISTINCT 把 NULL 也纳入唯一性判断,需要这种语义时用它,而不是在应用层兜。