MySQL 索引相关

2025/06/05

Categories: 技术 Tags: MySQL

InnoDB 的索引模型

根据叶子节点的内容,索引类型分为主键索引和非主键索引。

B+ 树和数据页

InnoDB 的数据是按数据页为单位来读写的,默认数据页大小为16 KB。每个数据页之间通过双向链表的形式组织起来,物理上不连续,但是逻辑上连续。

InnoDB 使用 B+ 树作为索引,B+ 树中的每个节点都是一个数据页,结构示意图如下:

「图片来自 小林 Coding

回表和覆盖索引

用表 T(id, k),id 为主键索引,k 为非主键索引,来举例分析:

由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。

最左前缀原则

当使用联合索引进行查询时,MySQL 可以利用该索引的最左前缀(最左边的一个或多个列)来加速查询。查询条件必须包含索引定义中从左到右的连续列,不能跳过中间列。

关键点:

索引下推

索引下推(Index Condition Pushdown)是 MySQL 5.6 版本引入的一项查询优化技术 ,主要用于优化使用非主键索引(二级索引)的查询过程。其核心思想是:将原本需要在 MySQL 服务器层处理的部分查询条件,下推到存储引擎层(如 InnoDB)在索引扫描阶段提前过滤数据,从而减少不必要的回表操作和数据传输量。

例如,对于联合索引 (A,B,C),查询语句 WHERE A=? AND C=? 来说,根据最左前缀原则,联合索引只能利用到 A 列:

相关问题

InnoDB 表无主键的情况下数据是如何存储的?

在 InnoDB 存储引擎中,每个表都必须有一个主键,即使在没有显式定义主键的情况下,InnoDB 也会通过以下机制自动选择或生成一个主键来组织数据存储:

数据始终以主键为依据,存储在聚簇索引的 B+ 树中。显式定义主键是更优的实践,可避免潜在的性能问题。

InnoDB B+ 索引树节点的键值是如何排序的?

不同字段类型的索引的排序规则由其数据类型和字符集排序规则决定:

对于联合索引的排序,首先按第一个字段 的排序规则进行整体排序;当第一个字段值相同时,再按第二个字段 的排序规则排序;依此类推,直到所有索引字段都参与排序。

索引失效有哪些情况?

  1. 不符合联合索引的非最左匹配原则。
  2. 对索引字段使用左或者左右模糊匹配(LIKE %xx 或者 LIKE %xx%)。因为 B+ 索引树是按照索引值有序排列存储的,只能根据前缀进行比较。
  3. 对索引字段使用函数(从 MySQL 8.0 开始,索引特性增加了函数索引,可以针对函数计算后的值建立一个索引,就可以通过扫描索引来查询数据)。
  4. 对索引字段进行表达式计算。
  5. 对索引字段隐式类型转换。MySQL 遇到字符串和数字比较的时候,会自动把字符串转为数字,然后再进行比较。如果字符串是索引列,而条件语句中的输入参数是数字的话,那么索引列会发生隐式类型转换,由于隐式类型转换是通过 CAST 函数实现的,等同于对索引列使用了函数,所以就会导致索引失效。
  6. WHERE 子句中的 OR,如果在 OR 前的条件列是索引列,而在 OR 后的条件列不是索引列,那么索引会失效。
  7. 对索引字段使用 NOT 或 !=。
  8. 数据量过少时优化器放弃使用索引。

MySQL Explain 执行计划结果解析?

内容摘抄自 JavaGuide,原文链接

mysql> EXPLAIN SELECT * FROM T WHERE id = 1;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | T     | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.000 sec)

可以看到,执行计划结果中共有 12 列,各列代表的含义总结如下表:

列名 含义
id SELECT 查询的序列标识符
select_type SELECT 关键字对应的查询类型
table 用到的表名
partitions 匹配的分区,对于未分区的表,值为 NULL
type 表的访问方法
possible_keys 可能用到的索引
key 实际用到的索引
key_len 所选索引的长度
ref 当使用索引等值查询时,与索引作比较的列或常量
rows 预计要读取的行数
filtered 按表条件过滤后,留存的记录数的百分比
Extra 附加信息

id

SELECT 标识符,用于标识每个 SELECT 语句的执行顺序。

id 如果相同,从上往下依次执行。id 不同,id 值越大,执行优先级越高,如果行引用其他行的并集结果,则该值可以为 NULL。

select_type

查询的类型,主要用于区分普通查询、联合查询、子查询等复杂的查询,常见的值有:

table

查询用到的表名,每行都有对应的表名,表名除了正常的表之外,也可能是以下列出的值:

type(重要)

查询执行的类型,描述了查询是如何执行的。所有值的顺序从最优到最差排序为:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

常见的几种类型具体含义如下:

possible_keys

possible_keys 列表示 MySQL 执行查询时可能用到的索引。如果这一列为 NULL,则表示没有可能用到的索引;这种情况下,需要检查 WHERE 语句中所使用的的列,看是否可以通过给这些列中某个或多个添加索引的方法来提高查询性能。

key(重要)

key 列表示 MySQL 实际使用到的索引。如果为 NULL 则表示未用到索引。

key_len

key_len 列表示 MySQL 实际使用的索引的最大长度;当使用到联合索引时,有可能是多个列的长度和。在满足需求的前提下越短越好。如果 key 列显示 NULL,则 key_len 列也显示 NULL。

rows

rows 列表示根据表统计信息及选用情况,大致估算出找到所需的记录或所需读取的行数,数值越小越好。

Extra(重要)

这列包含了 MySQL 解析查询的额外信息,通过这些信息,可以更准确的理解 MySQL 到底是如何执行查询的。常见的值如下:

这里提醒下,当 Extra 列包含 Using filesort 或 Using temporary 时,MySQL 的性能可能会存在问题,需要尽可能避免。

参考文档