暂无图片
暂无图片
13
暂无图片
暂无图片
暂无图片

SQL优化 - explain查看SQL执行计划(下)

原创 金同学 2025-05-06
722

上一篇文章(SQL优化 - explain查看SQL执行计划(上))介绍了explain执行计划中id、select_type、table、partition4个列的含义和优化建议,本文继续分享explain后半部分。

  1. id
  2. select_type
  3. table
  4. partitions
  5. type
  6. possible_keys
  7. key
  8. key_len
  9. ref
  10. rows
  11. filtered
  12. Extra

五、type列

访问表或者连接表的类型,性能从高到低。

字段值 含义
system 表中只有一条记录。
const 用主键或者唯一索引,与常数做等值查询,最多只有一行结果。
eq_ref 在多表连接查询时,使用主键或唯一索引字段连接表。
ref 用二级索引字段与常数做等值查询。
index_merge 使用索引合并的方式查询表,如并集、差集。
range 使用索引查询给定范围的数据。
index 扫描某个索引的全部记录。
all 全表扫描。

5.1 const

使用primary key或者unique取得一条数据。
特点:show warnings\G可以看到每个字段都已经显示为常数了。

mysql> explain select * from empplus where empno=7499; +----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+ | 1 | SIMPLE | empplus | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL | +----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec) mysql> show warnings; +-------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +-------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Note | 1003 | /* select#1 */ select '7499' AS `empno`,'ALLEN' AS `ename`,'SALESMAN' AS `job`,'7698' AS `mgr`,'1981-02-20' AS `hiredate`,'1600.00' AS `sal`,'300.00' AS `comm`,'30' AS `deptno` from `scott`.`empplus` where 1 | +-------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)

5.2 eq_ref

只出现在被驱动表上。 在join查询中,被驱动表的连接条件为unique key或primary key时出现。

mysql> explain select * from emp left join empplus on emp.empno=empplus.empno; +----+-------------+---------+------------+--------+---------------+---------+---------+-----------------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+--------+---------------+---------+---------+-----------------+------+----------+-------+ | 1 | SIMPLE | emp | NULL | ALL | NULL | NULL | NULL | NULL | 14 | 100.00 | NULL | | 1 | SIMPLE | empplus | NULL | eq_ref | PRIMARY | PRIMARY | 4 | scott.emp.empno | 1 | 100.00 | NULL | +----+-------------+---------+------------+--------+---------------+---------+---------+-----------------+------+----------+-------+ 2 rows in set, 1 warning (0.00 sec)

5.3 ref

驱动表、被驱动表上都会出现。表示对索引列的条件进行等值查询。

-- 示例:where deptno=10; mysql> explain select * from empplus where deptno=10 and hiredate like '1981%'; +----+-------------+---------+------------+------+---------------------+--------+---------+-------+-------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------------+--------+---------+-------+-------+----------+-------------+ | 1 | SIMPLE | empplus | NULL | ref | deptno,idx_hiredate | deptno | 5 | const | 49064 | 11.11 | Using where | +----+-------------+---------+------------+------+---------------------+--------+---------+-------+-------+----------+-------------+ 1 row in set, 2 warnings (0.00 sec)

扩展
执行计划中,只走了deptno列的索引,为什么key_len=5 ?
答:INT类型为4个字节,字段类型允许null时,还需要额外1个字节。

在联合索引中,ref可能存在性能问题,ref只能使用等号部分,非等值部分会引发二次过滤。当非等值部分扫描范围过大时,就会影响查询性能,。

-- 示例1:联合索引中的2个字段均为等值查询,通过key、key_len和filtered字段可以判定全部走了索引。 mysql> explain select * from emp where job='SALESMAN' and ename='ALLEN'; +----+-------------+-------+------------+------+---------------+---------------+---------+-------------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+---------------+---------+-------------+------+----------+-------+ | 1 | SIMPLE | emp | NULL | ref | idx_job_ename | idx_job_ename | 81 | const,const | 1 | 100.00 | NULL | +----+-------------+-------+------------+------+---------------+---------------+---------+-------------+------+----------+-------+ 1 row in set, 1 warning (0.01 sec)

联合索引中非等值部分没有走索引,需要二次过滤回表查询。但是针对这一现象,mysql在5.6版本引入了索引条件下推,即ICP优化(Index Condition Pushdown),后面展开讲。

-- 示例2:联合索引中的第1个字段等值查询,第2个字段非等值查询,通过key、key_len和filtered字段可以判定只有job字段走了索引。 mysql> explain select * from emp where job='SALESMAN' and ename like '%ALLEN'; +----+-------------+-------+------------+------+---------------+---------------+---------+-------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+---------------+---------+-------+------+----------+-----------------------+ | 1 | SIMPLE | emp | NULL | ref | idx_job_ename | idx_job_ename | 33 | const | 4 | 11.11 | Using index condition | +----+-------------+-------+------------+------+---------------+---------------+---------+-------+------+----------+-----------------------+ 1 row in set, 1 warning (0.00 sec)

5.4 range

索引范围查询主要出现在 >、<、between、in、like等

mysql> explain select * from dept d where d.deptno>20; +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | 1 | SIMPLE | d | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 2 | 100.00 | Using where | +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)

range只出现在驱动表,或者被驱动表中子查询的驱动表上。 被驱动表上是不可能用到range的。 示例中,range是 d.deptno>20 上产生的,另外被驱动表是ref,所以e表非等值条件没有用到索引。

mysql> explain select * from dept d straight_join emp e on d.deptno=e.deptno where d.deptno>20 and e.job='SALESMAN' and e.ename like '%ALLEN'; +----+-------------+-------+------------+-------+----------------------+---------------+---------+-------+------+----------+------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+----------------------+---------------+---------+-------+------+----------+------------------------------------+ | 1 | SIMPLE | d | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 2 | 100.00 | Using where | | 1 | SIMPLE | e | NULL | ref | deptno,idx_job_ename | idx_job_ename | 33 | const | 4 | 7.14 | Using index condition; Using where | +----+-------------+-------+------------+-------+----------------------+---------------+---------+-------+------+----------+------------------------------------+ 2 rows in set, 1 warning (0.01 sec)

5.5 index

索引全扫描比全表扫描 + order by的情况下快。using index的特点就是扫描很多行,但是因为不需要回表,所以IO较小。但是绝大部分情况下也属于优化对象。
<1> where条件没有索引,但是查询列在索引中或primary key包含查询列中时,会出现index。

mysql> explain select job from empplus where mgr=7902; +----+-------------+---------+------------+-------+---------------+-------------+---------+------+--------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------+-------------+---------+------+--------+----------+--------------------------+ | 1 | SIMPLE | empplus | NULL | index | NULL | idx_job_mgr | 38 | NULL | 114563 | 10.00 | Using where; Using index | +----+-------------+---------+------------+-------+---------------+-------------+---------+------+--------+----------+--------------------------+ 1 row in set, 1 warning (0.00 sec)

<2> 在聚合运算中,group by后面的列在索引或在primary key中,且查询列也在索引中时,会出现index。

mysql> explain select hiredate,count(*) from empplus group by hiredate order by hiredate; +----+-------------+---------+------------+-------+---------------------------------+--------------+---------+------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------------------------+--------------+---------+------+--------+----------+-------------+ | 1 | SIMPLE | empplus | NULL | index | idx_hiredate,idx_ename_hiredate | idx_hiredate | 4 | NULL | 114563 | 100.00 | Using index | +----+-------------+---------+------------+-------+---------------------------------+--------------+---------+------+--------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)

<3> 扩展:分组列和排序列不相同时,会出现 Using temporary; Using filesort 。性能影响极大,生产严禁出现。

mysql> explain select hiredate,count(*) from empplus group by hiredate order by job; +----+-------------+---------+------------+-------+---------------------------------+--------------+---------+------+--------+----------+---------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------------------------+--------------+---------+------+--------+----------+---------------------------------+ | 1 | SIMPLE | empplus | NULL | index | idx_hiredate,idx_ename_hiredate | idx_hiredate | 4 | NULL | 114563 | 100.00 | Using temporary; Using filesort | +----+-------------+---------+------------+-------+---------------------------------+--------------+---------+------+--------+----------+---------------------------------+ 1 row in set, 1 warning (0.00 sec)

5.6 all 全表扫描

一般情况下,全表扫描时非常糟糕的执行计划,除非大表查询40%以上的数据。

优化建议:条件无索引、对索引列进行加工、索引列隐式转换、对日期类型进行like 20%等都会引发全表扫描,写sql时尽可能避免。

六、possible_keys、key、key_len、ref列

6.1 possible_keys列

  • 列出可能用到的索引;
  • 关于auto_key,5.6之后的版本开始提供auto_key功能。所谓auto_key就是创建临时索引,需要消耗一些cpu和内存。对tmp_table_size,max_heap_table_size依赖较大。

6.2 key列

实际使用的索引,基于CBO的执行计划预估使用不同索引花费的成本,选出执行当前SQL成本最小的索引。

查看索引信息(show index from table; )
在联合索引中,第2列的cardinality是两个列非空唯一值的和;collation有2个值,A或者D,分别代表ASC(升序)、DESC(降序)。
image.png

6.3 key_len列

在联合索引中,通过key_len可以判断索引的使用率。
如下示例,可以看到key_len的长度不同,以此判断使用了哪些索引。

这里引用5.3章节的两段代码

-- 示例1:key_len=81 mysql> explain select * from emp where job='SALESMAN' and ename='ALLEN'; +----+-------------+-------+------------+------+---------------+---------------+---------+-------------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+---------------+---------+-------------+------+----------+-------+ | 1 | SIMPLE | emp | NULL | ref | idx_job_ename | idx_job_ename | 81 | const,const | 1 | 100.00 | NULL | +----+-------------+-------+------------+------+---------------+---------------+---------+-------------+------+----------+-------+ 1 row in set, 1 warning (0.01 sec)
-- 示例2:key_len=33 mysql> explain select * from emp where job='SALESMAN' and ename like '%ALLEN'; +----+-------------+-------+------------+------+---------------+---------------+---------+-------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+---------------+---------+-------+------+----------+-----------------------+ | 1 | SIMPLE | emp | NULL | ref | idx_job_ename | idx_job_ename | 33 | const | 4 | 11.11 | Using index condition | +----+-------------+-------+------------+------+---------------+---------------+---------+-------+------+----------+-----------------------+ 1 row in set, 1 warning (0.00 sec)

关于key_len计算

ename varchar(15) DEFAULT NULL COMMENT ‘雇员姓名’
job varchar(10) DEFAULT NULL COMMENT ‘雇员职位’

key_len1: (10 * 3 + 2 +1 ) + (15 * 3+ 2 +1) = 81
key_len2: 10 * 3 +2 +1= 33

  • 10 * 3 --> 10字符 * 3字节(utf8编码)
  • +2字节 --> 变长开销
  • +1字节 --> null标记

扩展常见数据类型的字节数
TINYINT: 1 字节
SMALLINT: 2 字节
MEDIUMINT: 3 字节
INT: 4 字节
BIGINT: 8 字节
FLOAT: 4 字节
DOUBLE: 8 字节
CHAR(M): M 字节, 其中 M 是字符数,最大值 255 字节
VARCHAR(M): L+2 字节, 其中 L 是实际字符数(utf8)
DATE: 3 字节
DATETIME: 8 字节
TIMESTAMP: 4 字节

6.4 ref列

如果是常数等值查询,‌显示const;‌如果是连接查询,‌显示被驱动表的关联字段。‌

七、rows列

mysql优化器根据统计信息预估出来的值。不准确
示例:rows为176184,这个值是预估索引emp_no通过range扫描出来的行。不包含回表过滤。即access过滤出来的,而不是filter条件过滤的。

mysql> select count(*) from empplus where empno > 7902; +----------+ | count(*) | +----------+ | 114675 | +----------+ 1 row in set (0.04 sec) mysql> explain select count(*) from empplus where empno > 7902; +----+-------------+---------+------------+-------+---------------+---------+---------+------+-------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------+---------+---------+------+-------+----------+--------------------------+ | 1 | SIMPLE | empplus | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 57281 | 100.00 | Using where; Using index | +----+-------------+---------+------------+-------+---------------+---------+---------+------+-------+----------+--------------------------+ 1 row in set, 1 warning (0.00 sec)

八、filtered列

跟rows一样,属于预估值。filtered的值非100时,extra列有using where关键字。它表示从innodb引擎中拿到数据后再加工的比率。这个比率也是我们创建良好索引的依据。

计算有效数据
表示innodb引擎通过索引idx_ename_hiredate预估扫描8192行数据,然后server层进行加工。
预估有效数据为:8192* 0.11 =>901行。

mysql> explain select * from empplus where ename='FORD' and hiredate like '1981%'; +----+-------------+---------+------------+------+---------------------------------+--------------------+---------+-------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------------------------+--------------------+---------+-------+------+----------+-----------------------+ | 1 | SIMPLE | empplus | NULL | ref | idx_hiredate,idx_ename_hiredate | idx_ename_hiredate | 48 | const | 8192 | 11.11 | Using index condition | +----+-------------+---------+------------+------+---------------------------------+--------------------+---------+-------+------+----------+-----------------------+ 1 row in set, 2 warnings (0.01 sec)

九、Extra列

9.1 distinct

mysql在join过程中,从i中取出一行后,查询j表的时候,碰到一行就停止,有点像exists。
image.png

distinct必要条件

  • select列必须由distinct关键字
  • select列上只能含有驱动表的字段
-- select列只有驱动表时,extra列会出现distinct关键字。 mysql> explain select distinct e.job from emp e left join dept d on d.deptno=e.deptno; +----+-------------+-------+------------+--------+---------------+---------+---------+----------------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+--------+---------------+---------+---------+----------------+------+----------+-----------------------+ | 1 | SIMPLE | e | NULL | ALL | idx_job_ename | NULL | NULL | NULL | 14 | 100.00 | Using temporary | | 1 | SIMPLE | d | NULL | eq_ref | PRIMARY | PRIMARY | 4 | scott.e.deptno | 1 | 100.00 | Using index; Distinct | +----+-------------+-------+------------+--------+---------------+---------+---------+----------------+------+----------+-----------------------+ 2 rows in set, 1 warning (0.01 sec)

本质上,distinct会对select子句中所有驱动表的列去重,当select列上有被驱动表字段,且该字段有重复值时,结果会出现一对多现象,即e.job对应多个d.name。此时,extra列不会显示distinct。

mysql> explain select distinct e.job,d.dname from emp e left join dept d on d.deptno=e.deptno; +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+ | 1 | SIMPLE | e | NULL | ALL | NULL | NULL | NULL | NULL | 14 | 100.00 | Using temporary | | 1 | SIMPLE | d | NULL | ALL | PRIMARY | NULL | NULL | NULL | 4 | 100.00 | Using where; Using join buffer (Block Nested Loop) | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+ mysql> select distinct e.job,d.dname from emp e left join dept d on d.deptno=e.deptno; +-----------+------------+ | job | dname | +-----------+------------+ | MANAGER | ACCOUNTING | | PRESIDENT | ACCOUNTING | | CLERK | ACCOUNTING | | CLERK | RESEARCH | | MANAGER | RESEARCH | | ANALYST | RESEARCH | | SALESMAN | SALES | | MANAGER | SALES | | CLERK | SALES | +-----------+------------+ 9 rows in set (0.00 sec)

select子句存在被驱动表列时,执行distinct的风险:
<1> 被驱动表字段若含NULL值,DISTINCT会将所有NULL视为相同值,可能掩盖实际数据差异;
<2> DISTINCT + JOIN可能无法利用索引下推(ICP)等优化;
<3> 被驱动表数据量较大时,DISTINCT需对所有字段组合排序去重,可能触发磁盘临时表,性能急剧下降。

9.2 using filesort 发生了排序

在进行order by,group by且没有使用索引时出现。排序参数:sort_buffer_size

mysql> explain select mgr,count(empno) from emp group by mgr order by 1; +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------+ | 1 | SIMPLE | emp | NULL | ALL | NULL | NULL | NULL | NULL | 14 | 100.00 | Using temporary; Using filesort | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------+ 1 row in set, 1 warning (0.00 sec) -- 添加索引后,filesort消失 alter table emp add index idx_mgr(mgr); mysql> explain select mgr,count(empno) from emp group by mgr order by 1; +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | 1 | SIMPLE | emp | NULL | index | idx_mgr | idx_mgr | 5 | NULL | 14 | 100.00 | Using index | +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)

9.3 using index 不回表

不用回表就可以查到数据
使用场景:where条件选择性不太好时,就可以考虑覆盖查询的列,来达到减少物理IO的目的。

下面sql走了索引idx_hiredate,因为empno是主键,跟在二级索引的后面。

mysql> explain select empno from emp; +----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+ | 1 | SIMPLE | emp | NULL | index | NULL | idx_hiredate | 4 | NULL | 14 | 100.00 | Using index | +----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.01 sec)

9.4 using temporary 使用了临时表

mysql执行过程中,为了存储中间的结果,会使用temporary table 。

<1> 如果执行计划中出现using temporary。使用temporary table后,我们无法判断是在内存中还是disk中。

  • order by,group by没有使用索引的时;
  • 执行计划中select_type为derived时;
  • show session status like ‘%tmp%’;

<2> 下面2个参数那个小就生效那个,木桶效应。

max_heap_table_size tmp_table_size

9.5 using where 发生了二次过滤

  • 一般using where要跟filtered、rows一起看
  • using where 表示从存储引擎中拿到一些数据,然后再过滤。
  • 其中rows是存储引擎中拿到预估值,filtered是在过滤的百分比。

关于对二次过滤的理解
using where表示执行器发生了二次过滤。innodb层扫描的数据返回给server层,server层通过条件过滤,这称为二次过滤。如果查询的字段都在索引,就需把索引中的行记录(即deptno、empno两个字段)返回server,否则需要回表,当整行数据返回server。

mysql使用覆盖索引后,为什么出现using where ?

image.png
image.png
一般来说,在范围扫描中,索引从左向右扫描。所以empno > 20定位时,start_key是开始条件,即索引定位,下图中的执行计划显示不正确。而empno < 20时,start_key从第一行开始扫描,这时empno < 20 只能作为filter过滤的条件,以及end_key,及查询结束的条件。

以下是debug总结出来的,很多执行计划显示是有误的,比如id >20 就可以指定定位数据,并不需要二次过滤。
image.png
image.png
总结:start key 后面是索引定位的条件。

很明显,empno >20中,start key是20。而如果条件为empno <20时,start key 就是索引的第一行了,而empno < 20就只能是filter过滤的条件了。
image.png

9.6 using index condition ICP

ICP技术允许innodb引擎在索引树上进行判断,‌如果数据不符合条件,‌则无需将该数据读取到server层判断,‌从而节省了磁盘I/O操作。‌这个优化技术让存储引擎在索引阶段就进行条件判断,‌实现了索引和where条件的早期过滤,‌减少了不必要的数据读取和处理,‌提高了查询性能。‌所以,在某种意义上,ICP实现了覆盖索引的功能

-- 示例 mysql> EXPLAIN SELECT * FROM people WHERE zipcode = '95054' AND lastname LIKE '%etrunia%'; +----+-------------+--------+------+---------------+-------------------+---------+-------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+---------------+-------------------+---------+-------+------+-----------------------+ | 1 | SIMPLE | people | ref | idx_zip_last_first | idx_zip_last_first | 32 | const | 5 | Using index condition | +----+-------------+--------+------+---------------+-------------------+---------+-------+------+-----------------------+

ICP的适用条件与限制
​适用场景:

  • 仅支持二级索引(InnoDB和MyISAM)
  • 查询需回表(非覆盖索引)
  • WHERE条件部分可被索引列过滤(如联合索引的中间列)

不适用场景:

  • 聚簇索引(InnoDB主键索引)
  • 虚拟生成列的二级索引
  • 条件包含子查询或存储函数

9.7 range checked for each record

一般情况下,隐式转换会出现这个问题。比如2个关联表字符集不匹配、排序规则不匹配、数据类型不匹配等。
执行show warnings就能看到发生隐式转换的详细信息。性能杀手

-- 示例 mysql> CREATE TABLE t1 (id VARCHAR(10), KEY(id)); mysql> CREATE TABLE t2 (id INT, KEY(id)); mysql> SELECT * FROM t1 JOIN t2 ON t1.id = t2.id; mysql> show warnings; Warning | 1739 | Cannot use ref access on index 'id' due to type conversion on field 't1.id' Note | 1003 | /* select#1 */ select `test`.`t1`.`id` AS `id`...

extra还有一些值,比如use MRR、using join buffer,这些都属于优化对象,不再做详细介绍。

image.png
欢迎关注作者公众号:类MySQL学堂

最后修改时间:2025-06-25 16:16:04
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

文章被以下合辑收录

评论