MySQL索引优化-如何使用索引

156 阅读4分钟

索引用于快速定位具有特定列值的行。如果没有索引,MySQL必须从第一行开始,逐行遍历整个表来查找相关行。表越大,这种操作的成本就越高。如果表对相关列有索引,MySQL可以快速确定在数据文件中的位置,而无需遍历所有数据。这比按顺序逐行读取要快得多。

大多数MySQL索引(包括主键、唯一键、普通索引和全文索引)存储在B+树中。

其它情况:

  1. 空间数据类型的索引使用R树。
  2. 内存表(MEMORY tables)也支持哈希索引。
  3. InnoDB使用倒排表来存储全文索引。

MySQL在以下操作中会使用索引:

  1. 快速查找符合WHERE子句的行。
  2. 如果一个查询涉及多个索引,MySQL会尽量使用能找到最少行数的索引(即选择性最高的索引)。
  3. 如果表有一个组合索引,优化器可以使用该索引的任意左前缀来查找行。例如,如果有一个关于(col1,col2,col3)的三列索引,那么就拥有针对(col1)、(col1,col2)和(col1,col2,col3)的索引搜索能力。
  4. 在执行连接操作(join)时从其他表中检索行。如果列被声明为相同的类型和长度,MySQL可以更高效地使用这些列上的索引。在这种情况下,如果VARCHARCHAR被声明为相同的长度,就被视为相同类型。例如,VARCHAR(10)CHAR(10)长度相同,但VARCHAR(10)CHAR(15)不同。
  5. 对于非二进制字符串列的比较,两个列应使用相同的字符集。例如,将一个utf8mb4列与一个latin1列进行比较时,将无法使用索引。
  6. 不同类型列之间的比较(例如,将一个字符串列与一个时间或数字列进行比较),如果值不能在不转换的情况下直接比较,可能不会使用到索引。对于数字列中的给定值(如1),它可能与字符串列中的多个值(如'1'、' 1'、'00001'或'01.e1')相等。这就排除了字符串列使用任何索引的可能性。
  7. 为特定的索引列key_col查找最小值(MIN())或最大值(MAX())时,预处理器会进行优化。该预处理器会检查在索引中出现在key_col之前的所有键部分是否都满足key_part_N = 常量条件。若满足此条件,MySQL 会针对每个MIN()MAX()表达式执行一次单键查找,并将其替换为常量。如果所有表达式都被替换成常量,查询将立即返回结果。例如:
SELECT MIN(key_part2), MAX(key_part2) FROM tbl_name WHERE key_part1=10;
  1. 当对表进行排序(ORDER BY)或分组(GROUP BY)时,如果操作基于某个可用索引的最左前缀列(例如 ORDER BY key_part1, key_part2),MySQL 可以利用该索引进行优化。此时,索引会按以下规则处理排序方向:
    • 如果所有索引键部分的排序方向均为默认的升序(ASC),则直接按索引的正序读取数据,无需额外排序操作。
    • 如果所有索引键部分均显式指定为降序(DESC),例如 ORDER BY key_part1 DESC, key_part2 DESC,则索引会以反向顺序读取数据。这种方式依然可以利用索引优化,避免全表排序。
    • 如果索引本身定义为降序索引(如 MySQL 8.0+ 支持的 INDEX (key_part1 DESC, key_part2 DESC)),则当查询的排序方向与索引定义一致时,索引会按正向顺序读取数据。这种情况下,即使查询中显式指定了 DESC,也可能无需反向扫描。

假设有索引 idx_key_parts (key_part1, key_part2)
-- 触发索引正向读取(默认升序)
SELECT * FROM tbl_name ORDER BY key_part1, key_part2;
-- 触发索引反向读取(显式降序)
SELECT * FROM tbl_name ORDER BY key_part1 DESC, key_part2 DESC;
-- 混合排序方向可能导致无法完全利用索引优化
SELECT * FROM tbl_name ORDER BY key_part1 ASC, key_part2 DESC;

  1. 当查询所需的列全部包含在某个索引中时(即使用覆盖索引),MySQL 可以直接从索引结构中获取数据而无需回表查询数据行,从而显著提升查询效率。

  2. 对于小型表或需要处理大部分/全部行的大型表(例如报表查询场景),索引的重要性会降低。当查询需要访问大部分数据行时,顺序读取数据的速度会高于通过索引检索的速度。顺序读取能最大限度地减少磁盘寻道次数,即便查询并不需要所有数据行。


参考文档: dev.mysql.com/doc/refman/…