MySQL 如何保证唯一性索引的唯一性?

索引数据结构(B+树)

  • 唯一性索引使用 B+树 结构存储键值,所有插入或更新的数据都会按照索引键值排序。
  • 当插入新数据时,MySQL 会遍历 B+树,检查是否存在相同的键值。如果存在,则拒绝操作。

隐式的唯一性检查

  • 插入/更新时的检查:每次对数据进行插入(INSERT)或更新(UPDATE)时,MySQL 会隐式执行唯一性验证。
  • 失败回滚:若检测到重复值,MySQL 抛出 ERROR 1062 (23000): Duplicate entry 错误,并回滚当前操作。

锁机制(并发控制)

  • 行级锁(InnoDB)
    InnoDB 引擎在唯一性检查时,会对涉及的索引记录加锁(如 行锁间隙锁),防止其他事务并发插入相同值。
  • 间隙锁(Gap Lock)
    在可重复读(REPEATABLE READ)隔离级别下,MySQL 会锁定可能引发冲突的“间隙”,避免幻读和并发唯一性冲突。

事务与隔离级别

  • 事务的隔离级别(如 READ COMMITTEDREPEATABLE READ)影响锁的持续时间和范围,确保在事务提交前,唯一性约束始终有效。
  • 例如,REPEATABLE READ 通过更强的锁机制(如间隙锁)严格保证唯一性。

唯一索引允许 NULL 值吗?

唯一性索引在 MySQL 中可以允许 NULL 值,但是 NULL 的表现是未知的。

在 InnoDB 存储引擎中,支持在唯一索引中有多个 NULL 值。这是因为在 MySQL 中,NULL 被认为是“未知”的,每个 NULL 值都被视为互不相同

Group BY 问题

在使用 GROUP BY 时,所有在 SELECT 子句中列出但未包含在聚合函数(如 COUNT(), SUM(), MAX()等)中的列都必须出现在 GROUP BY 子句中。否则就会报错

 1055 - Expression #1 of SELECT list is not in GROUP BY clause and contains 
 nonaggregated column 'light_oj.question_submit.id' 
 which is not functionally dependent on columns in GROUP BY clause; 
 this is incompatible with sql_mode=only_full_group_by

原因是 ONLY_FULL_GROUP_BY 模式被启用了

可以通过 SELECT @@global.sql_mode; 查看,如果结果中有,则表示开启了

Snipaste_2025-02-19_15-57-20.png

关闭 ONLY_FULL_GROUP_BY 模式

  1. 临时关闭
 SET SESSION sql_mode=(SELECT REPLACE(@@SESSION.sql_mode, 'ONLY_FULL_GROUP_BY', ''));
  1. 永久更改(需要管理员权限)

修改数据库配置文件通常是 my.cnfmy.ini

保存更改后,重启MySQL服务使更改生效。

 [mysqld]
 sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION