唯一索引是保障数据一致性的高效手段,适用于email、order_no等绝对不可重复的字段;建表时定义最清晰,添加前需查重清理;php中应捕获23000异常而非先查后插。

唯一索引是保障数据库数据一致性最直接、最高效的手段之一,尤其在用户注册、订单号生成、配置项键名等场景中,它能从根本上避免重复插入,比应用层判断更可靠、更原子。
什么时候该建唯一索引?
核心原则:只要业务逻辑要求某列(或几列组合)**绝对不能重复**,就应优先考虑唯一索引。常见典型场景包括:
- 用户表的
email或phone字段——同一邮箱只能注册一个账号 - 订单表的
order_no字段——每个订单号全局唯一 - 配置表的
key字段——配置项键名不可重复 - 多字段联合唯一,如
(user_id, product_id)表示一个用户对某商品只允许一条收藏记录
如何在 MySQL 中正确创建?
建表时定义最清晰,也便于团队理解约束意图:
CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, email VARCHAR(255) NOT NULL, username VARCHAR(50), UNIQUE KEY uk_email (email) );
已有表添加唯一索引需先确保数据无重复,否则会失败:
立即学习“PHP免费学习笔记(深入)”;
欢迎使用ChuangxinCMS企业网站管理系统软件! ChuangxinCMS是一个采用PHP技术和MYSQL数据库开发的企业网站管理系统,使用ChuangxinCMS能在最短的时间内花费最少的成本来搭建一个功能完善的企业网站,ChuangxinCMS具有一系列完善的内容管理功能,包括文章发布、分类管理、产品发布展示、下载模块等,整个系统页面设计简洁大方,功能实用高效,是中小型企业建站的最佳选择
-- 先查重 SELECT email, COUNT(*) FROM users GROUP BY email HAVING COUNT(*) > 1; <p>-- 清理或合并重复数据后执行 ALTER TABLE users ADD UNIQUE KEY uk_email (email);
注意:如果字段允许 NULL,MySQL 将把多个 NULL 视为不重复(符合 SQL 标准),如需严格限制“空值也不允许多个”,应在应用层校验或改用非空约束 + 唯一索引。
PHP 中如何优雅处理唯一索引冲突?
不要依赖 SELECT + INSERT 判断(竞态条件风险高),而是直接尝试插入,捕获数据库异常:
try {
$pdo->prepare("INSERT INTO users (email, username) VALUES (?, ?)")
->execute([$email, $username]);
} catch (PDOException $e) {
if ($e->getCode() === '23000' && strpos($e->getMessage(), 'Duplicate entry') !== false) {
throw new RuntimeException('邮箱已被注册');
}
throw $e;
}更推荐使用 MySQL 的 INSERT ... ON DUPLICATE KEY UPDATE 或 REPLACE INTO(注意后者是删再插,可能影响自增 ID 和外键),适合需要“存在则更新”的场景。
容易被忽略的关键细节
唯一索引不是万能的,实际使用中需注意:
-
前缀索引不支持唯一性保证:如
UNIQUE(email(10))只对前 10 字符去重,可能导致语义重复(如test123@abc.com和test123@def.com被认为相同) -
大小写敏感性取决于字段排序规则:
utf8mb4_unicode_ci是大小写不敏感的,utf8mb4_bin才区分大小写;若需邮箱严格区分大小写(极少见),需调整 collation -
NULL 值行为要明确:唯一索引允许任意数量的 NULL,这和多数业务直觉不符,建议配合
NOT NULL使用 - 性能开销真实存在:每次 INSERT/UPDATE 都需检查唯一性,高频写入表需评估索引维护成本










