10.3.1 MySQL 如何使用索引
索引用于快速找到具有特定列值的行。没有索引,MySQL 必须从第一个行开始,然后顺序读取整个表格以找到相关行。表越大,这种做法的成本就越高。如果表有为问题列设置的索引,MySQL 可以迅速确定在数据文件中寻找的位置,而无需查看所有数据。这比顺序阅读每一行都要快得多。
大多数 MySQL 索引(PRIMARY KEY
、UNIQUE
、INDEX
和 FULLTEXT
)都存储在B-树中。例外情况包括:空间数据类型的索引使用 R-树;MEMORY
表也支持哈希索引;InnoDB
使用逆向列表来处理 FULLTEXT
索引。
总的来说,索引用于以下讨论中描述的方式。特定于哈希索引(在 MEMORY
表中使用)的特性在第 10.3.9 节“B-树和哈希索引的比较”中进行了描述。
MySQL 使用索引执行以下操作:
-
快速找到匹配
WHERE
子句的行。 -
从考虑范围中排除行。如果有多个索引可供选择,MySQL 通常使用找到的行数最少(即最选择性的索引)的一个。
-
如果表格有多列索引,任何左侧的索引前缀都可以被优化器使用来查找行。例如,如果你在
(col1, col2, col3)
上创建了一个三列索引,你就可以对(col1)
、(col1, col2)
和(col1, col2, col3)
进行索引搜索。更多信息,请参阅第10.3.6节,“多列索引”。 -
在执行连接操作时,从其他表格检索行。MySQL可以更高效地使用那些类型和大小相同的列上的索引。在这种情况下,
VARCHAR
和CHAR
被认为是相同的,只要它们声明为相同的大小。例如,VARCHAR(10)
和CHAR(10)
是相同大小,但VARCHAR(10)
和CHAR(15)
不是。对于非二进制字符串列的比较,两个列都应该使用相同的字符集。例如,将一个
utf8mb4
列与一个latin1
列进行比较会排除索引的使用。不同类型的列之间进行比较(例如将字符串列与时间戳或数字列进行比较)可能会阻止使用索引,因为无法直接比较值而不需要转换。对于给定的数值如
1
在数字列中,它可能与字符串列中的任何数值相等,如'1'
,' 1'
,'00001'
或'01.01'
。这意味着无法使用任何索引来优化字符串列的查询。 -
为了找到特定索引列
key_col
的MIN()
或MAX()
值。这在预处理器的优化下进行,该预处理器检查您是否在所有发生在key_col
之前的索引部分上使用了WHERE
。在这种情况下,MySQL 对每个key_part_N
=constant
MIN()
或MAX()
表达式执行单一的键查找,并将其替换为常量。如果所有表达式都被替换为常量,查询就可以立即返回。例如:SELECT MIN(key_part2),MAX(key_part2) FROM tbl_name WHERE key_part1=10;
-
如果排序或分组是基于可用索引的一个左侧前缀进行的(例如,
ORDER BY
),那么可以对表进行排序或分组。如果所有关键部分后面都跟着key_part1
,key_part2
DESC第10.2.1.16节,“ORDER BY 优化”、第10.2.1.17节,“GROUP BY 优化”以及第10.3.13节,“降序索引”。
-
在某些情况下,查询可以被优化以不必咨询数据行就能检索值。(一个提供了所有必要结果的查询的索引称为覆盖索引。)如果查询只从表中使用那些包含在某个索引中的列,那么所选值可以从索引树中更快地检索到,以获得更高的速度:
SELECT key_part3 FROM tbl_name WHERE key_part1=1
对于小表或大表(即使是大表,只要报告查询处理了大多数或所有行),索引就不那么重要。当一个查询需要访问大部分行时,顺序读取比通过索引工作更快,因为它最小化了磁盘寻道,即使不是所有的行都用于查询。有关详细信息,请参阅第10.2.1.23节,“避免全表扫描”。