普通索引与唯一索引查询性能几乎无差别,差异主要体现在写入性能:唯一索引因需实时校验唯一性而无法使用change_buffer,导致更多磁盘IO;普通索引在写多读少场景更优,且应用层已保障唯一性时无需数据库层UNIQUE约束。

查得快不快,其实没差别
如果你只看 SELECT 性能,普通索引和唯一索引几乎一样快。InnoDB 按数据页(默认 16KB)读取,查到目标记录时,整页通常已在内存里;后续多判断一条“下个记录是否还满足条件”,对 CPU 来说就是一次指针跳转+一次比较——开销可忽略。
唯一索引确实会在找到第一条 k=5 后立刻停止;普通索引会继续往后扫,直到碰到第一个 k≠5 的记录。但只有当目标值恰好落在数据页末尾、且下一条在另一页时,才触发额外磁盘 IO——这种概率极低(千分之一量级),均摊后查性能差不到 0.1%。
写得多时,普通索引明显更省力
更新性能才是关键分水岭。核心在于 change_buffer:当目标数据页不在内存中时,普通索引能把更新先缓存在内存里,等页被读入后再合并(merge);而唯一索引必须校验唯一性,就得先把对应页从磁盘加载进来——相当于绕过了 change_buffer,多了一次随机磁盘读。
- 写多读少(如日志表、订单流水):选普通索引,
change_buffer能显著降低 IO 压力 - 读多写少(如配置表、字典表):
change_buffer反而可能增加 merge 开销,此时两者差距缩小,可按语义选 -
innodb_change_buffer_max_size默认 25(占 buffer pool 25%),可动态调,但对唯一索引无效
业务已保证唯一,就别用唯一索引
如果应用层(比如 Java 服务)已通过分布式锁、幂等设计、事务校验等方式确保不会插入重复值(例如身份证号、手机号),那字段本身具备逻辑唯一性,但没必要强加数据库层的 UNIQUE 约束。
原因很实在:
- 唯一索引会拖慢
INSERT/UPDATE,尤其高并发写入时,容易成为瓶颈 - MySQL 对唯一冲突的检查是全行扫描式校验,比普通索引多一次 B+ 树搜索路径
- 错误提示也更“重”:普通索引出错是业务逻辑问题;唯一索引报
Duplicate entry,常被误判为数据污染或并发 bug
典型反例:
ALTER TABLE user ADD INDEX idx_id_card (id_card);而不是
ALTER TABLE user ADD UNIQUE INDEX uk_id_card (id_card);
什么时候必须用唯一索引?
只有两种刚性场景才该上 UNIQUE:
- 需要数据库兜底防重,且应用层无法 100% 保障(比如多套系统直连 DB、遗留系统无幂等)
- 该字段要作为外键被其他表引用(InnoDB 要求被引用列必须有
UNIQUE或PRIMARY KEY)
注意:PRIMARY KEY 本质是 NOT NULL + UNIQUE,但每张表只能一个;而 UNIQUE 索引允许多个,也允许含 NULL(单个 UNIQUE 列可存多个 NULL,这是 MySQL 的行为)。
真正卡住人的从来不是“查得快不快”,而是半夜扩容时发现写入 QPS 掉了一半——回头一看,几十个本可普索引的字段全建了唯一索引。约束越重,代价越实;能靠代码守的界,就别让数据库替你扛。










