索引是一种数据结构,用于帮助我们在大量数据中快速定位到我们想要查找的数据,类似于书籍的目录

索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录

以下以 MySQL 8 为例说明:

1
2
3
4
5
6
7
8
-- 查看表索引
SHOW INDEX FROM table_name;

-- 创建索引
CREATE INDEX idx_name ON table_name(col1, col2);

-- 删除索引
DROP INDEX idx_name ON table_name;

索引详解

索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。

不同的存储引擎索引的结构也有区别:

  • InnoDB 存储引擎只有一个 .ibd 文件,涉及到事务的细粒度控制和并发安全考虑。

  • MyISAM 存储引擎存在 .MYI 文件中。所以 B+ 树索引的叶子节点并不存储数据,而是存储.MYD地址。

    image-20251222161007590

    添加索引后 .MYI 文件增大,可见索引是单独存储的

    image-20251222161142097

null 值索引可以重复,在mysql中,不算为值,跟空值有区别

1
2
3
4
 # 查看mysql中索引使用的统计情况  
# handler_read_key 表示使用索引查询到的次数
# handler_read_rnd_next 这个值越高,说明查询低效
show status like 'Handler_read%'

优点:大大提高了查询速度。

缺点:降低更新表的速度,如对表进行INSERTUPDATEDELETE时,MySQL不仅要保存数据,还要保存一下索引文件。且建立索引会占用磁盘空间。

以1000条数据为例,占用空间比对图:

image-20251222155145333

添加一个组合索引后,磁盘占用多了越160 KB

image-20251222154945316

在组合索引中减少字段,磁盘占用页相应减少,字段值越大占用空间越大。

image-20251222155359945

提示:在删除索引后,空间不会立刻被回收,需要执行optimize、repair或recreate后才会立刻回收

索引类型

  • B+ 树索引:是一种平衡多路查找树,所有数据记录都存储在叶子节点,非叶子节点只存索引键值和指向下一级节点的指针。

  • Hash 索引 :基于哈希表实现,对索引列计算哈希值,直接定位到数据位置。

  • 单列索引:即一个索引只包含单个列,一个表可以有多个单列索引

  • 组合索引:即一个索引包含多个列

  • 聚簇索引(聚集索引、主键索引):以 InnoDB 作为存储引擎的表,表中的数据都会有一个主键,即使你不创建主键,系统也会帮你创建一个隐式的主键。

  • 非聚簇索引(辅助索引或二级索引):以主键以外的列值作为键值构建的 B+ 树索引

image-20251212221800932

B+ 树索引

B+Tree 是一种平衡多路查找树,所有数据记录都存储在叶子节点,非叶子节点只保存索引键值和指向下一级节点的指针。

叶子节点之间通过指针连接,形成有序链表,支持高效的范围查询(如 BETWEEN><)和排序(ORDER BY)。

  • 支持等值查询(=)、范围查询、排序。

  • 查询复杂度为 O(log n)。

  • InnoDB 的主键索引(聚簇索引)就是 B+Tree。

B树特征

  • 每个节点都存储key和data。

  • 所有节点带有指向记录的指针,在内部节点出现的索引项不会再出现在叶子节点中。

  • 所有叶子节点指针为null,都是没有通过指针连接的。

  • 对于在内部节点的数据,可直接得到,不必根据叶子节点来定位。

B+树特征

1.单一节点存储更多的元素,使得查询的IO次数更少。(树的高度较低)

2.所有查询都要查找到叶子节点,查询性能稳定。(非叶子节点存储索引,叶子节点存储数据)

3.所有叶子节点形成有序链表,便于范围查询。(叶子节点是链表结构(顺序连接存储数据))

B+树优势

  • 非叶子节点只存储索引数据。所以每层能存更多索引数据(默认16K,通过show variables like 'innodb_page_size'命令查询),每次IO加载一层16K数据做索引筛选,筛选完成继续IO第二层

  • 叶子节点为有序的双向链表。当IO定位到叶子节点数据时,每次加载一页数据,当需要继续加载下一页数据时,无序再IO。

  • 三层能存储的数据量(int型的id为例,占8bit,指针占6bit,默认每页16k):
    16 * 1024 / (6 + 8) = 1170, 表示每页存的索引数量
    1170 * 1170 * 16 = 438400512 表示三层树可以存储的数据量(两层索引+一层数据,叶子节点的每个节点存储16k)

  • 一个千万量级,且存储引擎是MyISAM或者InnoDB的表,其索引树的高度在3~5之间。取决于索引占的大小

Hash 索引

基于哈希表实现,对索引列计算哈希值,直接定位到数据位置。

InnoDB中采用除法散列函数,冲突机制采用链接法(类似于Map集合,只是没有红黑树)。全表扫描方式

Hash索引是将索引键通过Hash运算之后,将 Hash运算结果的Hash值和所对应的行指针信息存放于一个Hash表中,由于不同索引键存在相同Hash值,所以即使取满足某个Hash键值的数据的记录条数,也无法从Hash索引中直接完成查询,还是要通过访问表中的实际数据进行相应的比较,并得到相应的结果。

  • 只支持等值查询(=IN

  • 不支持范围查询、排序、前缀匹配(如 LIKE 'abc%')。

  • 优化器不能使用 HASH 索引来加速 ORDER BY 操作。

  • 查询速度极快(O(1)),但冲突处理和哈希函数可能带来开销,所以不适合高重复值的列(如性别、状态等)。

和 BTree 的区别

  • Hash索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像B-Tree索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,所以Hash索引的查询效率要远高于B-Tree索引。

  • 哈希索引只支持等值比较查询(=、 IN 、<=>),不支持任何范围查询(因为经过Hash算法处理之后的Hash值大小关系,并不能保证和Hash运算前完全一样);

  • Hash索引无法避免数据排序操作;

  • Hash索引不能利用部分索引键查询(不支持多列联合索引的最左匹配规则 )

  • Hash索引需要全表扫描查询

  • 如果有大量重复键值的情况下,哈希索引的效率会很低,因为存在哈希碰撞问题

全文索引

全文索引(FULLTEXT),是基于倒排索引(Inverted Index),将文档中每个词映射到包含该词的文档列表。

用于对大文本字段(如 TEXTVARCHAR 进行关键词搜索。适用 InnoDB(MySQL 5.6+)、MyISAM 存储引擎

  • 支持自然语言搜索、布尔搜索。

  • 不支持普通 WHERE 条件(如 LIKE)。

使用方式

1
2
3
4
5
# 为 users 的 remarks 字段场景 FULLTEXT 索引
CREATE FULLTEXT INDEX idx_content ON users(remarks);

# 内容检索查询,默认以符号分词
SELECT * FROM users WHERE MATCH(remarks) AGAINST('更新备注');

会检索到如下数据,如果将检索换为 ‘备注’,则不会被检索到:

image-20251222143732907

MySQL 原生全文索引对中文支持极差,因为中文没有天然的空格分隔,且每个汉字都是“字母数字字符”。

MySQL 不会按语义分词,如果使用 utf8mb4 字符集,每个汉字被视为一个独立的“词”。但由于 最小词长限制(默认 ≥3),单个汉字长度为 1,会被直接丢弃。所以最终就根据符号分隔了,没有语义检索效果。

分词原理和规则

在建立和查询时会进行分词,但其分词机制相对简单,不依赖外部分词器(如中文常用的 jieba、IK 等),而是基于预定义的分隔符和停用词规则进行处理。

MySQL 全文索引的分词主要依据以下规则:

  • 以“非字母数字字符”作为分隔符

    • 默认将字母、数字、下划线(_)和美元符号($) 视为“词字符”

    • 其他字符(如空格、标点、中文标点、换行符等)被当作分隔符,用于切分词语

      Hello, world! How are you? 会被分为:['Hello', 'world', 'How', 'are', 'you']

  • 最小词长限制

    • MyISAM 引擎:由系统变量 ft_min_word_len 控制(默认为 4)。

    • InnoDB 引擎:由 innodb_ft_min_token_size 控制(默认为 3)。

      小于该长度的词会被直接忽略,不会被索引。如果 ft_min_word_len=4,则 “the”、“a”、“is” 等短词不会被索引。

      修改该参数后,必须重建全文索引才能生效。

  • 停用词过滤

    • MySQL 内置一个停用词列表(如 “the”, “and”, “or”, “is” 等常见无意义词),这些词即使长度达标,也不会被索引。
    • 默认停用词表包含约 36 个英文单词(InnoDB)或 700+ 个(MyISAM)。可通过自定义停用词表覆盖默认行为。

如何让 MySQL 全文索引支持中文?

  1. 预处理文本,人工插入分隔符:在应用层对中文进行分词(如用 jieba),然后用空格或特殊符号连接

  2. 降低最小词长 + 接受单字匹配:设置 innodb_ft_min_token_size = 1,这样就能搜单字,但无法理解词组语义

空间索引

空间索引(SPATIAL)基于 R-Tree(一种多维空间索引结构)。用于地理空间数据类型(如 POINTGEOMETRY)。

适用 MyISAM、InnoDB(MySQL 5.7+)存储引擎。要求列必须为 NOT NULL,且使用空间数据类型。

聚簇/主键索引

每张表只能拥有一个聚簇索引。可以存在多个 null

聚簇索引就是按照每张表的主键构造一颗B+树,同时叶子节点中存放的就是整张表的行记录数据,也将聚集索引的叶子节点称为数据页。这个特性决定了索引组织表中数据也是索引的一部分;

聚集规则:有主键则定义主键索引为聚集索引;没有主键则选第一个不允许为NULL的唯一索引;还没有就使用innodb的内置rowid为聚集索引。 主键如果更改代价较高,故建表时要考虑自增ID不能频繁update这点,且最好有顺序(数字)。

优点:

  • 数据访问更快,因为聚簇索引将索引和数据保存在同一个B+树中,从聚簇索引中获取数据更快
  • 对主键的排序查找和范围查找非常快
  • 叶子节点存储全部数据(以)

缺点:

  • 插入速度严重依赖于插入顺序,按照主键的顺序插入是最快的方式,否则将会出现页分裂,严重影响性能。因此,对于InnoDB表,我们一般都会定义一个自增的ID列为主键

  • 更新主键的代价很高,因为将会导致被更新的行移动。因此,对于InnoDB表,一般定义主键为不可更改

  • 二级索引访问需要两次索引查找,第一次找到主键值,第二次根据主键值找到行数据。

查找原理:

1
select * from user where id >= 18 and id < 40    # 以主键id范围查找

(注:一般数据库默认都会为主键生成聚簇索引,一般根节点都是常驻内存的,也就是说页 1 已经在内存中)

img
  1. 首先 找到 id=18 的键值,对应指针为p2,定位到页3。

  2. 将页3数据读入内存,找到索引键值18,拿到对应的指针,定位到页8(第一次IO)。

  3. 将页8数据读入内存,找到索引键值18,拿到符合的数据。(第二次IO,叶子节点数据都是链表顺序连接的)

  4. 将页8中数据遍历查找符合条件的。

  5. 根据页8的指针,读取页9数据到内存,继续遍历符合的数据,以此类推(第三次IO)。

最终我们找到满足条件的所有数据,总共 12 条记录:

(18,kl), (19,kl), (22,hj), (24,io), (25,vg) , (29,jk), (31,jk) , (33,rt) , (34,ty) , (35,yu) , (37,rt) , (39,rt) 。

非聚簇/非主键索引

又称辅助索引、二级索引、非聚集,是除主键以外的其他索引。

聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要二次查找(回表)。辅助索引叶子节点存储的不再是行的物理位置,而是主键值。

Innodb辅助索引的叶子节点并不包含行记录的全部数据,叶子节点除了包含键值外,还包含了相应行数据的聚簇索引键。以便于拿到该键值二次查询聚簇索引。

查找原理

通过辅助索引首先找到的是主键值,再通过主键值找到数据行的数据页,再通过数据页中的Page Directory找到数据行。

在叶子节点中,不再存储所有的数据了,存储的是键值和主键,如,叶子节点中的 x-y(索引键值 - 主键值)。

查找的流程跟聚集索引一样,最终找到主键后,需要再到聚集索引中查找具体对应的数据信息,此时又回到了聚集索引的查找流程。

组合索引

使用组合索引,需要遵守最左前缀原则:查询条件必须从最左边的列开始,才能有效利用索引。

例如:索引 (a, b, c) 可用于 WHERE a=1 AND b=2,但不能用于 WHERE b=2WHERE c=3

但是在 Mysql 8中,在优化器做了大量优化,在符合场景的数据也可能会用到索引,具体参考:MySQL 8.0 新增功能函数和使用案例

  • where子句中使用最频繁的一列放在最左边,因为MySQL索引查询会遵循最左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。

  • 将离散度高的列放前面,可以过滤更多的数据,使得扫描行数更少,过滤掉的数据更多。

覆盖索引

如果查询的所有列都在索引中,无需回表查询数据行,性能极高(从索引中就能够取得所需数据,不必从数据表中读取)。

SELECT id from orders WHERE status = 'PAID' – id 在索引中,无需再到叶子结点查询数据节点,称之为实现了索引覆盖。

避免了回表操作,减少I/O提高效率。MySQL 只能使用B+Tree索引做覆盖索引,因为只有B+树能存储索引列值。

  • 辅助索引不包含行数据的所有信息,故其大小远小于聚簇索引,因此可以减少大量的IO操作。

  • 索引覆盖只需要扫描一次索引树,不需要回表扫描聚簇索引树,所以性能比回表查询要高。

  • 索引中列值是按顺序存储的,索引覆盖能避免范围查询回表带来的大量随机IO操作。

    判断一条语句是否用到索引覆盖: explain 命令中的 Extra列中包含 Using index 就表示使用到了索引覆盖

索引下推

处理包含过滤条件的查询语句。是MySQL5.6推出来的一个查询优化方案,主要是减少数据库中不必要的数据读取和计算。

作用:本该在 server 层进行筛选的条件,下推到存储引擎层进行筛选判断,能有效减少回表(下推的前提是查询条件都是索引列)

原理:在使用索引进行查询时,将查询的过滤条件也应用到索引查找过程中,以减少需要读取和处理的数据量,从而提高查询性能。

1
2
3
# 默认情况下,索引下推处于启用状态。
set optimizer_switch='index_condition_pushdown=off'; -- 关闭索引下推
set optimizer_switch='index_condition_pushdown=on'; -- 开启索引下推

所以,应该尽可能的建立联合索引,而不是独立的索引。既可以节省空间,又可以提高查询效率(索引覆盖、索引下推)。

示例: select * from t_user where name like 'L%' and age = 17; 其中name 和age为一个联合索引

不用索引下推的执行过程:

第一步:利用索引找出name带’L’的数据行:LiLei、Lili、Lisa、Lucy 这四条索引数据第二步:再根据这四条索引数据 id 值,逐一进行回表扫描,从聚簇索引中找到相应的行数据,将找到的行数据返回给 server 层。第三步:在server层判断age = 17,进行筛选,最终只留下 Lucy 用户的数据信息。第四步:将符合条件的索引对应的 id 进行回表扫描,最终将找到的行数据返回给 server 层。

用索引下推的执行过程:

第一步:利用索引找出name带’L’的数据行:LiLei、Lili、Lisa、Lucy 这四条索引数据第二步:根据 age = 17 这个条件,对四条索引数据进行判断筛选,最终只留下 Lucy 用户的数据信息。(注意:这一步不是直接进行回表操作,而是根据 age = 17 这个条件,对四条索引数据进行判断筛选)第三步:将符合条件的索引对应的 id 进行回表扫描,最终将找到的行数据返回给 server 层。

8.0 索引优化

mysql 8 对索引做了大量优化,具体参考:MySQL 8.0 新增功能函数和使用案例

  • 索引跳跃扫描( index skip scan,针对最左匹配原则的优化)

  • 隐藏索引(新增)

  • 降序索引(新增)

  • 函数索引(新增)

规则与失效

失效场景

什么情况下索引不会被命中

  • 查询条件没有使用索引列

  • where条件带有 or ,即使其中有条件带索引也不会使用(想让索引生效,只能将or条件中的每个列都加上索引,如果OR的其中一个条件没有建立索引,则都不会使用索引,建议使用 union ,拼接多个查询语句)。

  • like 以%开头,索引不会命中(除非只查询索引列),如:“%name”。

  • 范围查询条件右边的列索引失效,如:age > 20 and name = ‘test’

  • 不等于(!= 或者<>)索引失效(8.0 版本以后支持)

  • 类型转换:如果列类型是字符串,一定要在条件中将数据使用引号引用起来,否则不使用索引。

  • 函数计算:查询条件中,在索引列上使用函数(+, - ,*,/), 这种情况下需建立函数索引。

  • 采用 not in, not exist (8.0 版本以后支持,以前不会命中,采用的也是当前字段的索引)。

  • 采用 is not null (8.0 版本以后支持,以前不会命中,采用的也是当前字段的索引)。

  • 散列小被优化:索引列的散列较小,如,tinyint,bool等,选择和查询的筛选后的数据量过大,经过优化器后可能会全表扫描。

选择与规则

是否需要建立索引、值得建立索引?哪些字段适合建立索引?

MySQL是没有计算索引选择性的,只是预测逻辑IO操作的数量,因为索引不止消耗磁盘空间,也会耗费性能,每次的DDL操作也会同时更新索引。因此对于MySQL索引要慎重选择。

索引选择性占比:结果个数与总个数的比值,(x / sum) > 20。

一般来说,如果选择性超过 20% 那么全表扫描比使用索引性能更优。

  • 选择高区分度的列建索引(如用户ID,而非性别)。

  • 避免在索引列上使用函数或表达式,如 WHERE YEAR(create_time) = 2024 会导致索引失效。

  • 合理使用组合索引,遵循最左前缀原则。

  • 监控索引使用情况:通过 EXPLAIN 分析执行计划。

  • 避免过度索引:索引会占用空间,并降低 INSERT/UPDATE/DELETE 性能。

1
2
3
4
5
6
7
8
计算公式
SELECT COUNT(*) FROM table_name WHERE column_name / SELECT COUNT(*) FROM table_name
如:
SELECT (SELECT COUNT(*) FROM sys_dock_recycle_drop WHERE `code` = 'FC1299653255') /
(SELECT COUNT(*) FROM sys_dock_recycle_drop) * 100

再如:
SELECT (SELECT COUNT(*) FROM zssec_water_history WHERE dev_status > 0) / (SELECT COUNT(*) FROM zssec_water_history) * 100

不适合建立索引的列

  • tinyint 或 用以标致类型的int列。比如用以保存性别,选择性过大,最小都是超过25%了,因此没有设置索引的必要。

  • 更新频繁的字段。

  • 不会出现在where子句中的字段。

  • 数据可能为null或空或0的字段

  • 存储占用过大的,如:text,json

适合建立索引的列

  • 字段离散度越高,越适合选作索引的关键字。如主键和唯一字段(主键索引自动创建)。

  • 很少更新的字段(因为也会牵连更新索引)。

  • 占用存储空间少的字段。

  • 存储空间固定的字段。

  • Where子句中经常使用的字段,分组字段、排序字段和多表连接字段(外键索引自动创建)。

  • 尽量使用联合索引(前缀)

最左匹配规则(联合索引)

最左优先,以最左边的为起点任何连续的索引都能匹配上。右边索引需要依赖左表索引的执行结果,否则数据无法正常查询。因为索引结构存储是顺序存储的,先根据最左边索引排序,再依次根据后面排序。

同时遇到范围查询(>、<、between、like)就会停止匹配。

所以,将范围性和模糊等不确定性查询条件,放置靠后位置。 如: where a = 1 and d in (2,3) and c > 4 and b like 5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 8.0之前版本:
# 如:建组合索引 a,b,c ,相当于建立了 a、a,b、a,b,c 三组索引

# 可以使用组合索引
SELECT * FROM test WHERE a="1" AND b="2"
SELECT * FROM test WHERE a="1"
# 不会使用到组合索引,8.0中可能会用到
SELECT * FROM test WHERE b="1" AND c="2"
SELECT * FROM test WHERE c="1"

# 以下查询只用到组合索引的一部分,8.0中可能会用到
where a = xxx and c = xxx # 可以用到 a 列的索引,用不到 c 列索引。
where a like 'xxx%' and b = xxx # 可以用到 a 列的索引,用不到 b 列的索引。
where a > xxx and b = xxx # 可以用到 a 列的索引,用不到 b 列的索引。

# 如下查询条件完全用不到索引,8.0中可能会用到
where b = xxx
where c = xxx
where a like '%xxx' -- 不满足最左前缀
where d = xxx order by a -- 出现非排序使用到的索引列 d
where a + 1 = xxx -- 使用函数、运算表达式及类型隐式转换等

EXPLAIN 详解

EXPLAIN 是分析和优化 SQL 查询性能的重要工具,以下为EXPLAIN 输出中各列的含义,带***最重要,**次之:

image-20251211175337674

type(访问类型 ***)

按性能从优到劣排序:

  • 最优级别

    • system:表只有一行记录(系统表)

    • const:通过主键或唯一索引一次就找到

      SELECT * FROM users WHERE id = 1;

  • 优秀级别

    • eq_ref:唯一索引扫描,通常用于多表 JOIN 之间的外键索引

      SELECT * FROM users u JOIN orders o ON u.id = o.user_id;

  • 良好级别

    • ref:非唯一索引扫描,返回匹配某个值的所有行

      EXPLAIN SELECT * FROM users WHERE phone_number = '14524043463'

    • fulltext:全文索引扫描

    • ref_or_null:类似 ref,但包含 NULL 值搜索

  • 一般级别

    • range:索引范围扫描(BETWEEN、IN、>、< 等)

      EXPLAIN SELECT * FROM users WHERE id BETWEEN 1997867665955237888 AND 1997869822548984078;

      image-20251212221047183
  • 需要优化

    • index:全索引扫描(读取整个索引)

      1
      2
      3
      EXPLAIN SELECT id FROM users WHERE MOD(id,5)=0 or MOD(id,6)=0 limit 100, 1000
      # 可以优化,从 index -> range
      EXPLAIN SELECT id FROM users WHERE id > 1997869822548984078 AND (MOD(id,5)=0 or MOD(id,6)=0) limit 100, 1000
    • ALL:全表扫描(不使用索引,性能最差,需要优化)

      1
      2
      3
      4
      # 改为这样,从 index -> ALL
      EXPLAIN SELECT * FROM users WHERE MOD(id,5)=0 or MOD(id,6)=0 limit 100, 1000
      # 可以优化,从 index -> range
      EXPLAIN SELECT * FROM users WHERE id > 1997869 AND (MOD(id,5)=0 or MOD(id,6)=0) limit 100, 1000

Extra(额外信息 ***)

提供额外的执行信息:

  • Using index:覆盖索引直接返回结果id,无需回表其他非索引字段。

    image-20251212214456150
  • Using index condition:索引条件下推(ICP), 将 WHERE 条件中索引列的过滤下推到存储引擎层执行。可以减少回表次数,提高查询性能

    image-20251212215959149 image-20251212220034919

需要关注和优化的问题

  • Using where:在存储引擎检索后再过滤相关数据

    image-20251212214643683
  • Using temporary:使用临时表(可能需要优化)

    image-20251212220745766

    优化排序后

    image-20251212220838751
  • Using filesort:使用文件排序(需要优化)

    image-20251212220315311
  • Using join buffer:使用连接缓冲区

    多表连接查询,如果type = ALL ,这是因为相关表缺少外键连接索引,则使用连接缓冲区。

    有外键索引的 type = ref,并显示具体的索引名信息。

    image-20251222103120147

ref(索引引用 **)

显示索引的哪一列被使用

  • const:常量

  • 列名:使用的列

  • func:函数

select_type(查询类型 **)

  • SIMPLE:简单 SELECT,不使用 UNION 或子查询

  • PRIMARY:查询中最外层的 SELECT

  • SUBQUERY:子查询中的第一个 SELECT

  • DERIVED:派生表(FROM 子句中的子查询)

  • UNION:UNION 中的第二个或后续 SELECT

  • UNION RESULT:UNION 的结果

  • DEPENDENT SUBQUERY:依赖外部查询的子查询

rows(预估扫描行数 *)

MySQL 预估需要扫描的行数(值越小越好)

filtered(过滤比例 *)

存储引擎层返回的数据在服务层过滤后,剩余的比例(百分比值,越大越好)

id(查询标识符)

SELECT 查询的序列号

SELECT * FROM users WHERE id IN (SELECT user_id FROM orders); 子查询的 id 会不同

  • id 相同:按顺序执行

  • id 不同:id 值越大,优先级越高,越先执行

  • id 为 NULL:表示 UNION 结果或子查询

key(实际用的索引)

查询实际使用的索引。如果为 NULL,表示没有使用索引

key_len(索引长度)

使用的索引字节数。可判断是否使用了索引的全部部分

复合索引 (name, age), 如果只用到 name,key_len 会较短

possible_keys(可能用的索引)

查询可能使用的索引列表,如果为 NULL,考虑添加合适的索引

table(访问的表)

显示查询涉及的表名。可能是实际表名、别名或 <derivedN><unionM,N>