MySQL Execution Plan--执行计划中的Type列

本文深入解析了SQL执行计划中的type列含义,通过具体案例对比了index range scan与index scan的区别,解释了IN子句如何影响索引扫描方式,以及如何通过MySQL trace工具观察执行计划的细节。

在一次的优化过程中,由于没有关注执行计划中type列,仅看key列来查看"使用到的索引",导致优化过程走了不少弯路。

以下面SQL为例:

SELECT wave_no,
SUM(IF(picking_qty IS NULL, 0, picking_qty)) AS PICKED_QTY,
SUM(IF(differ_qty IS NULL, 0, differ_qty)) AS PICKED_DIFFER_QTY,
SUM(IF(relocate_qty IS NULL, 0, relocate_qty)) AS PICKED_RELOCATE_QTY 
FROM picking_locate_d
WHERE yn = 0
AND wave_no IN
(
'BC76361213164811',
'BC76361213164810',
...
'BC76361213158692'
)
AND org_No = '661'
AND distribute_No = '763'
AND warehouse_No = '612' 
GROUP BY wave_no;

走索引查找的执行计划为:

+----+-------------+------------------+------------+-------+---------------+-------------+---------+------+-------+----------+------------------------------------+
| id | select_type | table            | partitions | type  | possible_keys | key         | key_len | ref  | rows  | filtered | Extra                              |
+----+-------------+------------------+------------+-------+---------------+-------------+---------+------+-------+----------+------------------------------------+
|  1 | SIMPLE      | picking_locate_d | NULL       | range | idx_wave_no   | idx_wave_no | 153     | NULL | 14238 |     0.01 | Using index condition; Using where |
+----+-------------+------------------+------------+-------+---------------+-------------+---------+------+-------+----------+------------------------------------+

 走索引扫描执行计划为:

+----+-------------+------------------+------------+-------+---------------+-------------+---------+------+----------+----------+-------------+
| id | select_type | table            | partitions | type  | possible_keys | key         | key_len | ref  | rows     | filtered | Extra       |
+----+-------------+------------------+------------+-------+---------------+-------------+---------+------+----------+----------+-------------+
|  1 | SIMPLE      | picking_locate_d | NULL       | index | idx_wave_no   | idx_wave_no | 153     | NULL | 37660147 |     0.01 | Using where |
+----+-------------+------------------+------------+-------+---------------+-------------+---------+------+----------+----------+-------------+

 

上面两个执行计划都使用索引idx_wave_no,但:

第一个执行计划影响行数为14238,与IN查询中的值数量相同,其执行计划type列值为range,表示index range scan。

第二个执行计划影响行数为37660147,与整表数据量相同,其执行计划type列为index,表示index scan。

 

哪为啥索引查找是index range scan呢?通过MySQL trace工具查看,其中输出包含以下信息:

"chosen_range_access_summary": {
"range_access_plan": {
  "type": "range_scan",
  "index": "idx_wave_no",
  "rows": 5,
  "ranges": [
    "BC76361213164810 <= wave_no <= BC76361213164810",
    "BC76361213164811 <= wave_no <= BC76361213164811",
    "BC76361213158692 <= wave_no <= BC76361213158692"
  ] /* ranges */
} /* range_access_plan */,
"rows_for_plan": 5,
"cost_for_plan": 9.01,
"chosen": true
} /* chosen_range_access_summary */

其中查询中WHERE子句:

wave_no IN
(
'BC76361213164811',
'BC76361213164810',
'BC76361213158692'
)

由于idx_wave_no为非唯一索引,虽然是等值查询,仍需要从第一个等于指定值的索引记录开始扫描,直到第一个不等于指定值的索引记录,因为被称为范围扫描(Range Scan) :

"ranges": [
    "BC76361213164810 <= wave_no <= BC76361213164810",
    "BC76361213164811 <= wave_no <= BC76361213164811",
    "BC76361213158692 <= wave_no <= BC76361213158692"
  ] 

IN子句中的3个值被转换为3次INDEX RANGE SCAN。

 

对于全索引扫描(INDEX SCAN),通过MySQL trace工具查看,其中输出包含以下信息:

"considered_execution_plans": [
  {
    "plan_prefix": [
    ] /* plan_prefix */,
    "table": "`picking_locate_d`",
    "best_access_path": {
      "considered_access_paths": [
        {
          "rows_to_scan": 37660147,
          "access_type": "scan",
          "resulting_rows": 3.77e7,
          "cost": 9.58e6,
          "chosen": true,
          "use_tmp_table": true
        }
      ] /* considered_access_paths */
    } /* best_access_path */,
    "condition_filtering_pct": 100,
    "rows_for_plan": 3.77e7,
    "cost_for_plan": 9.58e6,
    "sort_cost": 3.77e7,
    "new_cost_for_plan": 4.72e7,
    "chosen": true
  }
] /* considered_execution_plans */

其中access_type=scan表明操作为INDEX SCAN,rows_to_scan=37660147表名扫描整个索引上37660147行记录。

 

通过DESC或EXPLAIN输出的执行计划中,Type列的可选值分别对应:

all: 全表扫描
index: 索引全扫描
range: 索引范围扫描,常用语<,<=,>=,between等操作
ref: 使用非唯一索引扫描或唯一索引前缀扫描,返回单条记录,常出现在关联查询中
eq_ref: 类似ref,区别在于使用的是唯一索引,使用主键的关联查询
const/system: 单条记录,系统会把匹配行中的其他列作为常数处理,如主键或唯一索引查询
null: MySQL不访问任何表或索引,直接返回结果

 

转载于:https://www.cnblogs.com/gaogao67/p/10771113.html

### MySQL 执行计划的概念 MySQL 执行计划Execution Plan)是指查询优化器在处理 SQL 语句时生成的一种详细计划,用于描述数据库如何具体执行该查询。执行计划的核心目标是通过基于成本和规则的优化,选择一种执行成本最低的方式[^3]。这种方式不仅决定了查询的效率,还直接影响数据库的整体性能。 执行计划通常由 `EXPLAIN` 语句生成,它以表格形式展示查询的执行细节,包括访问表的顺序、使用的索引、连接类型以及其他相关信息[^1]。 --- ### MySQL 执行计划的功能 1. **优化查询性能** 执行计划能够揭示查询的具体执行路径,帮助开发者识别低效的操作,例如全表扫描、不合理的索引使用或不当的连接顺序。通过分析这些信息,可以调整 SQL 语句或数据库结构以提高性能[^2]。 2. **诊断性能瓶颈** 当某些查询响应时间过长时,执行计划可以帮助定位问题所在。例如,如果发现某张表没有使用索引而进行了全表扫描,这可能是一个潜在的性能瓶颈[^3]。 3. **指导索引设计** 执行计划能够明确指出哪些字段被频繁用作查询条件或排序依据,从而为创建合适的索引提供参考。合理的设计索引可以显著减少查询时间[^1]。 4. **评估连接策略** 对于涉及多表的复杂查询,执行计划会显示每张表的访问顺序以及连接方式(如 Nested Loop Join、Hash Join 等)。这有助于判断是否选择了最优的驱动表和连接算法[^3]。 5. **辅助学习与调试** 即使在正常运行的情况下,查看执行计划也能帮助开发者更深入地理解 SQL 查询的工作原理,从而写出更加高效和规范的代码。 --- ### 示例:使用 EXPLAIN 查看执行计划 以下是一个简单的示例,展示如何通过 `EXPLAIN` 查看 SQL 查询的执行计划: ```sql EXPLAIN SELECT * FROM users WHERE age > 30; ``` 输出结果可能包含以下字段(部分字段含义如下): - `id`: 查询的序号,表示查询的选择顺序。 - `select_type`: 查询的类型,例如 SIMPLE、PRIMARY、SUBQUERY 等。 - `table`: 当前操作的表名称。 - `type`: 访问类型,例如 ALL(全表扫描)、INDEX(索引扫描)、RANGE(范围扫描)等。 - `possible_keys`: 可能使用的索引。 - `key`: 实际使用的索引。 - `rows`: 预估需要扫描的行数。 - `Extra`: 其他信息,例如 Using where、Using index 等。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值