MySQLinnodb引擎普通索引和唯一索引的区别

InnoDB 中普通索引(非唯一索引,Non-Unique Index)和唯一索引(Unique Index)的核心区别在于 是否强制索引键的唯一性约束,这导致了它们在行为、性能和维护上存在重要差异:

核心区别

  1. 唯一性约束 (Uniqueness Constraint):
    • 唯一索引: 强制索引键值的 唯一性 。尝试插入或更新数据导致索引键值重复时,操作会失败并抛出 Duplicate entry 错误。
    • 普通索引: 不强制唯一性约束。允许索引键值重复(即多行记录可以拥有相同的索引键值)。这是最本质的区别。

由唯一性约束衍生的差异

  1. 数据插入/更新时的行为:

    • 唯一索引:
      • 插入或更新涉及索引键时,必须立即检查该键值是否已存在于索引中(以确保唯一性)。
      • 这个检查通常需要 读取索引页(可能涉及磁盘 I/O,如果页不在 Buffer Pool 中)。
      • 如果检测到重复,操作立即失败。
    • 普通索引:
      • 插入或更新时,不需要立即检查键值是否重复(因为允许重复)。
      • 写入操作(特别是 INSERTUPDATE 导致索引键变化时)可以更"宽松"地进入系统。
  2. Change Buffer (变更缓冲区) 的利用:

    • 唯一索引: 无法利用 Change Buffer。
      • 原因:Change Buffer 的核心思想是延迟非唯一的二级索引的写入操作。因为唯一索引必须在插入/更新时立即检查唯一性,而这个检查需要访问索引页(确认没有重复),所以无法将变更缓存在内存中再合并,必须同步(sync)地将变更应用到索引页。
    • 普通索引: 可以利用 Change Buffer。
      • 对于 INSERT, DELETE, UPDATE 操作引起的普通二级索引页的变更,如果目标索引页不在 Buffer Pool 中,InnoDB 可以将这些变更暂时缓存到 Change Buffer (内存中),而不是立即从磁盘读取索引页进行修改。
      • 等到后续需要读取该索引页到 Buffer Pool 时,或者系统空闲时 ,或者后台线程定期刷新时,再将 Change Buffer 中的变更合并(Merge)到实际的索引页上。
      • 优势: 显著减少随机磁盘 I/O(避免立即读磁盘),提高写入性能(尤其是大量插入/更新时)。对于机械硬盘(HDD)提升尤其明显。
      • 配置: 通过 innodb_change_buffering 控制(all, none, inserts, deletes, changes/purges, 默认通常是 all)。通过 innodb_change_buffer_max_size 控制其占 Buffer Pool 的比例(默认 25%)。
  3. 锁机制 (Locking):

    • 唯一索引: 在检查唯一性时(如 INSERT ... ON DUPLICATE KEY UPDATE 或可能导致重复的插入),可能会使用 Next-Key Lock(间隙锁+记录锁)来锁定可能产生重复的"间隙",以防止其他事务在检查期间插入导致重复的值。这锁的范围可能略大。
    • 普通索引: 通常只需要对具体的记录加锁(Record Lock 或 Next-Key Lock 锁住该记录本身和它前面的间隙),因为不关心唯一性,锁的范围通常更精确(只针对实际存在的重复值行)。但插入大量重复值时,锁竞争也可能很激烈。
  4. 查询优化器的考虑 (Query Optimizer):

    • 唯一索引: 优化器知道每个键值最多只对应一行数据。这在某些查询(如 MIN(), MAX()LIMIT 1 结合 ORDER BY 索引列)中可以提供微小的优化潜力,因为它知道找到第一个/最后一个键值就确定了结果。
    • 普通索引: 优化器需要考虑到一个键值可能对应多行数据。
  5. 作为外键约束的目标:

    • 唯一索引: 可以 被其他表的外键约束引用(REFERENCES)。外键列必须引用父表的一个唯一约束(主键或唯一索引)。
    • 普通索引: 不能直接作为外键约束的目标。引用普通索引会导致错误,因为它无法保证被引用行的唯一性。
  6. NULL 值的处理:

    • 唯一索引: 在 MySQL 中,唯一索引允许多个 NULL 值 。这是因为 SQL 标准规定 NULL 不等于任何值,包括另一个 NULL。所以,(col1=NULL)(col1=NULL) 的比较结果是 UNKNOWN,不违反唯一性约束。唯一索引列可以包含多个 NULL
    • 普通索引: 当然也允许多个 NULL 值,且允许多个相同的非 NULL 值。

总结对比表

特性 唯一索引 (Unique Index) 普通索引 (Non-Unique Index)
唯一性约束 ✅ 强制,键值必须唯一 ❌ 不强制,允许多行相同键值
Change Buffer 支持 无法利用,变更必须同步写入索引页 可以利用,提高写入性能 (减少磁盘 I/O)
插入/更新检查 必须立即检查唯一性 (可能读磁盘) 无需立即检查唯一性
锁范围 (插入时) 可能略大 (Next-Key Lock 防幻读/唯一检查) 通常更精确 (锁具体记录/间隙)
NULL 值处理 ✅ 允许多个 NULL ✅ 允许多个 NULL 和多个相同非 NULL
可作为外键目标 ✅ 可以 ❌ 不可以
优化器考虑 (唯一性) 知道键值唯一,可能有微优化 需考虑键值不唯一
主要目的 保证数据唯一性,实现业务约束/参照完整性 加速基于该列的查询/排序/分组,不关心唯一性

选择建议

  • 需要强制业务唯一性时,必须使用唯一索引: 例如用户邮箱、身份证号、订单号等需要绝对唯一的字段。这是保证数据正确性的关键。
  • 追求更高写入性能且允许重复时,使用普通索引: 特别是当目标索引页经常不在 Buffer Pool 中(如非常大的表),且写入负载很高时,普通索引利用 Change Buffer 的优势非常明显。
  • 外键引用必须指向唯一索引 (通常是主键或唯一索引)。
  • 主键 (PRIMARY KEY) 是一种特殊的唯一索引: 它强制唯一+非空,并且是表的聚簇索引。

简单来说:唯一索引用约束换安全,普通索引用约束换性能(写入)。 在设计表结构时,应根据业务逻辑对数据唯一性的要求和性能需求(尤其是写性能)来权衡选择哪种索引类型。

相关推荐
电商API_1800790524714 分钟前
大规模调用淘宝商品详情 API 的分布式请求调度实践
服务器·数据库·分布式·爬虫
Menior_1 小时前
【补充】数据库中有关系统编码和校验规则的简述
数据库·mysql·oracle
晴子呀1 小时前
分库分表和sql的进阶用法总结
数据库·sql
编程(变成)小辣鸡1 小时前
Redis 知识点与应用场景
数据库·redis·缓存
Kay_Liang2 小时前
从聚合到透视:SQL 窗口函数的系统解读
大数据·数据库·sql·mysql·数据分析·窗口函数
H2122021652 小时前
SQLite3库链接与加载问题解决方案
数据库·oracle·sqlite
IT毕设实战小研2 小时前
基于Spring Boot校园二手交易平台系统设计与实现 二手交易系统 交易平台小程序
java·数据库·vue.js·spring boot·后端·小程序·课程设计
倔强的石头_2 小时前
KingbaseES高可用架构深度解析——从读写分离到异地灾备的全方位守护
数据库
范纹杉想快点毕业3 小时前
嵌入式 C 语言编程规范个人学习笔记,参考华为《C 语言编程规范》
linux·服务器·数据库·笔记·单片机·嵌入式硬件·fpga开发