【Mysql】唯一索引【一】前置基础【二】唯一索引底层原理【1】结构组成【2】唯一性约束怎么实现1插入 / 更新前校验核心2加锁逻辑和普通索引最大差异3唯一索引 vs 主键索引4联合唯一索引【3】使用唯一索引后会不会出现重复数据【三】优缺点【1】优点【2】缺点【四】补充【一】前置基础InnoDB 存储引擎下1所有索引都是B 树2主键是聚簇索引数据行本身存在主键叶子节点3普通索引、唯一索引都是二级索引辅助索引。【二】唯一索引底层原理【1】结构组成唯一索引 B 树叶子节点存储[索引键值主键值]索引键建索引的字段要求全局唯一允许 1 个 NULL主键用于回表查询完整行数据对比普通二级索引结构完全一样唯一区别是写入时唯一性校验逻辑。【2】唯一性约束怎么实现1插入 / 更新前校验核心执行 INSERT / UPDATE 时InnoDB 通过 B 树快速查找当前索引键是否已存在1-根据索引值定位到对应叶子页2-页内二分查找匹配记录3-若找到相同非 NULL 值 → 直接抛出 Duplicate entry4-不存在则正常写入。注意唯一索引允许多条 NULL不仅允许一条 NULL逻辑NULL ! NULL理论上能存多个 NULL但 InnoDB 限制唯一索引最多一条 NULL。2加锁逻辑和普通索引最大差异1普通二级索引范围扫描会加临键锁可能幻读2唯一索引等值匹配精准命中唯一一条数据时退化为行锁不会锁间隙并发更好找不到数据才会加间隙锁防止插入。3唯一索引 vs 主键索引1主键索引PRIMARY1-聚簇索引叶子存完整行2-不允许 NULL3-一张表只能一个2UNIQUE 唯一索引1-二级索引叶子只存主键2-允许单个 NULL3-一张表可多个4-需要回表拿数据。4联合唯一索引UNIQUE idx_a_b(a,b)唯一性判定是组合整体唯一单字段可重复只要组合不重复即可。底层 B 树按 (a,b) 联合值排序插入时校验组合值全局唯一。【3】使用唯一索引后会不会出现重复数据1场景 1字段存 NULL多条空记录最常见业务上你认为 “手机号不能为空”但代码没做校验数据库允许多条手机号 NULL看起来是重复数据。解决业务层判空 字段加 NOT NULL。2场景 2并发插入无分布式锁分库分表 / 多实例单库单表 InnoDB 中唯一索引自带行锁并发插入同一值会阻塞不会重复。但以下架构会产生重复分库分表唯一索引仅单表生效不同分片可以存相同唯一值多库 / 多实例无全局锁两个服务同时校验不存在并行插入本地唯一索引互不感知最终两条重复数据读写分离读从库校验再写主库从库同步延迟读到旧数据导致重复插入。单库 InnoDB 不会出现这个问题底层唯一索引写入时加排他锁拦截并发。方案1字段非空唯一业务字段添加 NOT NULL杜绝多条 NULL2单库场景依赖唯一索引兜底InnoDB 单表并发插入天然防重3分布式 / 分库分表方案 A全局唯一 ID雪花号替代手机号等业务唯一键方案 BRedis 分布式锁 唯一索引双重校验方案 C统一路由分片字段把唯一键作为分片键【三】优缺点【1】优点1自动实现唯一性约束数据库层面兜底不用业务代码判断去重防止并发场景下重复数据业务查询校验存在并发漏洞。2等值查询性能优于普通索引等值精准匹配时数据库明确最多一条数据匹配到即停止遍历普通索引要读完所有相同值。3减少业务编码复杂度不用额外加分布式锁、SELECT 判断去重降低重复写入 bug 概率。4支持快速去重检索DISTINCT 查询、去重统计会直接利用唯一索引优化无需额外排序。5锁粒度更小并发更高等值命中唯一记录时只加行锁无间隙锁减少锁冲突、死锁概率。【2】缺点1写入性能损耗更大每次插入 / 更新都必须做唯一性查找校验多一次 B 树查询大批量导入数据时速度明显慢于普通索引。2失效场景多容易引发死锁 / 锁等待插入冲突时会阻塞等待持有锁的事务提交高并发插入重复值会大量锁等待拖慢接口。3不适合高频变更字段更新唯一索引字段会导致 B 树节点分裂、页移动产生大量页分裂、undo 日志、redo 日志IO 飙升。4NULL 行为容易踩坑只能存一条 NULL如果业务需要多字段可空且整体唯一逻辑容易混乱。5占用额外存储空间作为二级索引独立一棵 B 树相比无索引多存储开销联合唯一索引存储成本更高。6批量导入场景阻塞严重大量数据导入有重复风险时唯一性校验串行化导入速度远低于普通索引大批量数据建议先删唯一索引导入完成再重建。7无法使用 change buffer 优化写入关键性能短板InnoDB change buffer 用来缓存普通索引的写操作减少随机 IO唯一索引不能使用 change buffer每次写入必须立刻刷磁盘校验唯一性随机 IO 暴涨写入压力大。【四】补充1唯一索引 vs 业务唯一约束代码去重只用代码去重高并发下存在竞态条件同一时刻两条请求同时查询无数据同时插入产生重复只用唯一索引写入失败会抛异常业务需要捕获 Duplicate entry 异常做重试 / 提示。2推荐用唯一索引手机号、身份证、订单号、设备唯一标识、用户唯一业务编码等强唯一业务字段。3不推荐唯一索引日表、日志表批量导入海量数据频繁更新的业务字段仅做查询、无强去重要求的字段高并发实时写入、吞吐敏感场景可业务层分布式锁兜底。