目录
[1. 不满足"最左前缀法则"(针对联合索引)](#1. 不满足“最左前缀法则”(针对联合索引))
[2. 在索引列上做运算或使用函数](#2. 在索引列上做运算或使用函数)
[3. 隐式类型转换(最容易踩的坑)](#3. 隐式类型转换(最容易踩的坑))
[4. LIKE 模糊查询以 % 开头](#4. LIKE 模糊查询以 % 开头)
[5. OR 关联的条件中,有一侧没有索引](#5. OR 关联的条件中,有一侧没有索引)
索引 是帮助MySQL高效获取数据的数据结构(提高性能)
MySQL在存储数据之外,数据库系统中还维护着满足特定查找算法的数据结构,这些数据结构以某种引用(指向)表中的数据,这样我们就可以通过数据结构上实现的高级查找算法来快速找到我们想要的数据。而这种数据结构 就是索引。
不用加内存,不用改程序,不用调sql,只要执行正确的create index ,查询速度就可能提高成百上千倍。但是天下没有免费的午餐,查询速度的提高是以插入、更新、删除的速度为代价的,这些写操作,增加了大量的IO。
- InnoDB 存储引擎的每次磁盘 I/O 操作读取或写入的大小是一页(16KB)
- B+树的结构下每个节点的大小就是一页(16KB)
- 树的高度 == 每次查询数据时磁盘IO操作的次数
- MySQL 会默认按照主键给我们的数据进行排序
二、索引的数据结构
哈希表
- 优点:等值查询时效率很高,时间复杂度为O(1)
- 缺点:不支持范围快速查找,范围查找时还是只能通过扫描全表方式
二叉树
- 问题:由于二叉树的特性,当数据量大时,树的层级一定很高,那么查询时磁盘IO操作一定很多

二叉搜索树可能退化成为线性结构

AVL &&红黑树?虽然是平衡或者近似平衡,但是毕竟是二叉结构,相比较多阶B+,意味着树整体
过高,大家都是自顶向下找,层高越低,意味着系统与硬盘更少的IO Page交互。
B树(多叉平衡查找树 聚簇索引)
- 特点 :非叶子节点存储了 索引数据 和 记录数据
- 优点:由于是多叉树,当数据量大时,树的层级较低
- 缺点:由于节点中存储的是 索引+记录,且记录数据的大小往往远超索引数据大小,且由于一个节点的大小即为一个页的大小16KB,所以一个节点中能存储的空间大部分被记录数据占用了,那么树的分叉(分支因子)一定会小,即树的高度会高,查询时IO次数多

B+树(多叉平衡查找树 非聚簇索引)
- 特点:
- 非叶子节点只存储索引数据,所有数据(索引+记录)存储在叶子节点, => 分支因子大,树的层数低
- 在叶子节点之间形成了双向链表 => 便于范围查询
- 优点:
- 分支因子大(即一个节点有很多个子节点),树的高度低,查询时IO次数少
- 支持高效的范围查找


三、聚集索引和二级索引
聚集索引:主键索引, 它的叶子节点存储了完整的整行数据。(如果表中没有设置主键,则聚集索引是 第一个不为null的唯一索引)
二级索引:非主键索引
聚集索引树(主键索引树)的叶子节点存储的是:索引数据+整行记录
二级索引树的叶子节点存储的是:索引数据+主键值
四、回表
sql
age为二级索引,回表
select id,age,name from tb.student where age = 1;
当查询语句中 以二级索引的列 age 为查询条件时,会先走二级索引树,查到叶子节点取到主键值,
再回到主键索引树上,以刚取到的主键值去匹配,找到叶子节点取得 id、age、name
上述过程中查询主键索引树的这个过程叫回表,需要尽量避免回表
五、覆盖索引



六、索引失效的情况
只要你的查询条件让 MySQL 无法顺利沿着 B+ 树的顺序往下找数据,索引就会失效。
1. 不满足"最左前缀法则"(针对联合索引)
如果你创建了一个联合索引 (a, b, c),就相当于创建了一本按照 a 排序,a 相同时按 b 排序,b 相同时按 c 排序的字典。
-
失效情况: 你的查询条件跳过了最左边的列,直接用后面的列。
-- 失效:直接用 b 和 c,没用 a,MySQL 没法在第一层进行定位 SELECT * FROM table WHERE b = 1 AND c = 2; -
如何解决: 保证
WHERE条件中必须包含联合索引的最左第一列 。只要包含了第一列,哪怕顺序乱了(比如WHERE c=2 AND a=1),MySQL 的优化器也会自动帮你调整顺序以命中索引。
2. 在索引列上做运算或使用函数
B+ 树里存的是列的原始值,一旦你对列做了运算,原有的顺序就被破坏了。
-
失效情况:
-- 失效:对索引列 age 做了加法运算 SELECT * FROM user WHERE age + 1 = 10; -- 失效:对索引列 create_time 使用了函数 SELECT * FROM user WHERE MONTH(create_time) = 5; -
如何解决: 把运算和函数移到等号右侧,保证等号左侧只有纯净的字段名。
-- 解决:运算放到右边 SELECT * FROM user WHERE age = 10 - 1; -- 解决:用范围查询替代时间函数 SELECT * FROM user WHERE create_time >= '2023-05-01' AND create_time < '2023-06-01';
3. 隐式类型转换(最容易踩的坑)
当字段类型与传入值的类型不一致时,MySQL 会偷偷在底层帮你加个转换函数,这等同于在索引列上用了函数。
-
失效情况: 假设
phone字段是VARCHAR(字符串)类型,但你传入了一个整数。-- 失效:字符串字段传了数字,MySQL 底层会把字段值转换成数字再比较 SELECT * FROM user WHERE phone = 13800138000; -
如何解决: 严格匹配数据类型,字符串一定要加单引号。
-- 解决: SELECT * FROM user WHERE phone = '13800138000';
4. LIKE 模糊查询以 % 开头
B+ 树只能从字符串的最左边开始匹配顺序。
-
失效情况:
-- 命中索引:前缀确定,可以走 B+ 树查找 SELECT * FROM user WHERE name LIKE '张%'; -- 失效:最左边是未知的,B+ 树彻底懵了,只能全表扫描 SELECT * FROM user WHERE name LIKE '%三'; -
如何解决:
-
业务上尽量避免
%在前的模糊匹配。 -
如果必须用,且对性能要求高,可以考虑引入 Elasticsearch 这样的全文搜索引擎。
-
如果只是查询少量字段,且这些字段都在联合索引中,可以利用我们之前说的覆盖索引来弥补全表扫描的损耗。
-
为什么有最左前缀法则
补一张图帮助理解,表示联合索引的索引树
之所以有最左前缀法则,是因为联合索引的索引树是这样排列的,当缺少了左侧列为查询条件时,右侧字段列无法根据索引树去查找数据,所以索引失效

5. OR 关联的条件中,有一侧没有索引
-
失效情况: 假设
id有索引,但age没有索引。-- 失效:即使 id 有索引,但因为 age 没索引,MySQL 为了找出符合 age=10 的数据依然要全表扫描,那干脆就一次性全表扫描算了。 SELECT * FROM user WHERE id = 1 OR age = 10; -
如何解决:
-
确保
OR左右两侧的列都建立了索引。 -
或者将
OR改写为UNION ALL结合单条件查询。
-