MySQL 如何保证唯一性索引的唯一性?
索引数据结构(B+树)
- 唯一性索引使用 B+树 结构存储键值,所有插入或更新的数据都会按照索引键值排序。
- 当插入新数据时,MySQL 会遍历 B+树,检查是否存在相同的键值。如果存在,则拒绝操作。
隐式的唯一性检查
- 插入/更新时的检查:每次对数据进行插入(
INSERT
)或更新(UPDATE
)时,MySQL 会隐式执行唯一性验证。 - 失败回滚:若检测到重复值,MySQL 抛出
ERROR 1062 (23000): Duplicate entry
错误,并回滚当前操作。
锁机制(并发控制)
- 行级锁(InnoDB):
InnoDB 引擎在唯一性检查时,会对涉及的索引记录加锁(如行锁
或间隙锁
),防止其他事务并发插入相同值。 - 间隙锁(Gap Lock):
在可重复读(REPEATABLE READ
)隔离级别下,MySQL 会锁定可能引发冲突的“间隙”,避免幻读和并发唯一性冲突。
事务与隔离级别
- 事务的隔离级别(如
READ COMMITTED
、REPEATABLE 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;
查看,如果结果中有,则表示开启了
关闭 ONLY_FULL_GROUP_BY 模式
- 临时关闭
SET SESSION sql_mode=(SELECT REPLACE(@@SESSION.sql_mode, 'ONLY_FULL_GROUP_BY', ''));
- 永久更改(需要管理员权限)
修改数据库配置文件通常是 my.cnf
或 my.ini
保存更改后,重启MySQL服务使更改生效。
[mysqld]
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION