在 MySQL 中,InnoDB 是最常用的存储引擎,它支持事务、行级锁和外键约束等功能,而索引则是提升数据库查询性能的关键。在 InnoDB 存储引擎中,索引不仅仅是提高查询速度的工具,还是数据库的核心组成部分之一。本文将详细介绍 InnoDB 存储引擎的索引结构、索引种类、索引优化技巧以及索引失效等方面的知识。
在 InnoDB 存储引擎中,索引主要分为两种类型:聚集索引(Clustered Index)和非聚集索引(Non-clustered Index)。
InnoDB 存储引擎中的聚集索引和非聚集索引通常都是基于 B+ 树(自平衡的树状数据结构)来实现的。B+ 树有以下特点:
InnoDB 支持以下几种类型的索引:
GEOMETRY
)进行查询优化。它使用的是 R 树(Region Tree)而不是 B+ 树。InnoDB 索引遵循 最左前缀原则。也就是说,当你使用复合索引(由多个列组成的索引)时,索引的查询可以使用到最左侧的一部分索引。
举个例子:
假设有一个复合索引 (a, b, c)
,那么查询时,可以利用以下的前缀索引:
(a)
:只使用列 a
,是有效的。(a, b)
:使用列 a
和列 b
,是有效的。(a, b, c)
:使用列 a
、b
和 c
,是有效的。但如果你只使用 (b)
或 (c)
作为查询条件,MySQL 将无法使用这个复合索引,因为它没有按照最左前缀的顺序来查询。
索引覆盖是指查询中涉及到的所有列都包含在索引中,从而避免了对表数据的访问。换句话说,查询的数据完全通过索引获取,无需回表操作。
举个例子:
假设有一个复合索引 (a, b, c)
,并且你执行了如下查询:
SELECT a, b FROM table WHERE a = 1;
如果索引 (a, b, c)
已经包含了查询所需的列 a
和 b
,那么 MySQL 可以直接从索引中获取数据,而不需要回到表中去查询。
索引覆盖的好处是能显著提高查询性能,尤其是在大数据量的表中。
索引下推是一种优化技术,它能够将查询条件推送到存储引擎的索引扫描阶段,而不是等到读取数据行时再进行过滤。这样可以减少需要读取的数据行数量,提升查询效率。
例如,
SELECT * FROM Employees WHERE age > 30 AND salary like '%5000'
其中联合索引(age、salary);
没有索引下推时,执行这条语句的流程:
1、存储引擎使用联合索引查出age>30的二级索引数据(叶子节点中有age、salary、主键);
2、拿到主键回表,到聚簇索引中拿到完整记录;
3、将所有的完整记录返回到server层(服务器层),再进行salary的模糊查询。
开启索引下推后,执行流程:
1、存储引擎使用联合索引查出age>30的二级索引数据(叶子节点中有age、salary、主键);
2、直接在二级索引数据中对salary进行模糊查询。
可以看出索引下推之后减少了回表的次数,从而降低了查询的时间。
索引合并是 MySQL 在某些情况下使用的一个优化技术。当查询条件涉及多个索引时,MySQL 会尝试将多个索引的结果合并起来,从而加快查询速度。
举个例子:
假设有两个索引:idx_a
和 idx_b
,查询条件是:
SELECT * FROM table WHERE a = 1 OR b = 2;
MySQL 可以使用索引合并策略,首先分别从 idx_a
和 idx_b
中找到符合条件的记录,然后将它们合并,最后返回结果。
索引并不是总能在所有情况下发挥作用。在以下情况下,索引可能会失效:
!=
、<>
)SELECT * FROM table WHERE a != 1;
索引无法有效利用,因为不等于操作会导致扫描整个数据集。
OR
连接多个条件SELECT * FROM table WHERE a = 1 OR b = 2;
当查询条件中包含多个列的 OR
时,索引可能无法有效地优化查询,尤其是当 OR
连接的列没有单独建立索引时。
SELECT * FROM table WHERE YEAR(date_column) = 2023;
在where语句中使用函数(如 YEAR()
)会导致索引失效,因为索引是基于列值进行查找的,函数的使用会改变查询模式。
7.4 LIKE
前缀不匹配
SELECT * FROM table WHERE name LIKE '%abc';
如果 LIKE
查询以通配符 %
开头,索引通常无法被使用,因为 MySQL 无法利用前缀进行快速查找。
可以通过以下方式查看 MySQL 表的索引信息:
SHOW INDEX FROM table_name;
该语句将列出表 table_name
中所有的索引,包括索引的名称、类型、涉及的列等信息。
也可以使用explain关键字查看一条SQL的执行计划:
EXPLAIN SELECT * FROM users WHERE age > 30;
结果如下:
id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
---|
1 | SIMPLE | users | range | idx_age | idx_age | 4 | NULL | 2 | Using where |
这个执行计划的解释为:
1
表示这是一个简单查询。SIMPLE
表示这是一个简单的查询(没有子查询)。users
表。range
表示 MySQL 使用了范围扫描(age > 30
是一个范围条件),这是比 ALL
更高效的扫描类型。NULL
)。idx_age
索引,意味着查询能够利用索引进行更高效的检索。Using where
表示在扫描每一行时,MySQL 使用了 WHERE
子句的条件进行过滤。LIKE
、OR
、!=
等会导致索引失效的操作。EXPLAIN
语句来分析查询的执行计划,检查索引的使用情况,并根据结果调整索引设计。