执行计划中的COLLECTION ITERATOR PICKLER FETCH导致的性能问题

今天开发的同事找到我,让我评估一个sql语句。因为这条语句被应用监控组给抓取出来了,需要尽快进行性能调优。
sql语句比较长,是由几个Union连接起来的子查询。
xxxxx
UNION
  SELECT /*+ leading (ar1_creditid_tab ar1_unapplied_credit) use_nl (ar1_creditid_tab ar1_unapplied_credit) */
           UNIQUE
           0,
           MAX (uc.credit_id) credit_id,
           0,
           0,
           0,
           SUM (uc.unapplied_amount) allocated_amount,
           TO_DATE ('') due_date,
           'Unapplied',
           '0',
           transaction_id
    FROM   ar1_unapplied_credit uc,
           (SELECT   COLUMN_VALUE AS credit_id
              FROM   table(SELECT   CAST (:5 AS ar1_numberarray_tp) credit_id
                             FROM   DUAL)) ar1_creditid_tab
   WHERE       uc.reversal_trans_id IS NULL
           AND uc.credit_id = ar1_creditid_tab.credit_id
           AND uc.partition_id = NVL (:6, 0)
           AND uc.credit_type LIKE :7
GROUP BY   uc.transaction_id

执行计划如下所示,可以看到资源消耗还是很高的。
Plan hash value: 3920442503

-----------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                  | Name                       | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                           |                            |     2 |   184 |   368K (12)| 01:13:48 |       |       |
|   1 |  SORT UNIQUE                               |                            |     2 |   184 |   368K (12)| 01:13:48 |       |       |
|   2 |   UNION-ALL                                |                            |       |       |            |          |       |       |
|   3 |    HASH GROUP BY                           |                            |     1 |   145 |   325K  (1)| 01:05:04 |       |       |
|   4 |     NESTED LOOPS                           |                            |       |       |            |          |       |       |
|   5 |      NESTED LOOPS                          |                            |     1 |   145 |   325K  (1)| 01:05:04 |       |       |
|   6 |       NESTED LOOPS                         |                            |     1 |   130 |   325K  (1)| 01:05:03 |       |       |
|   7 |        NESTED LOOPS                        |                            |     1 |    80 |   325K  (1)| 01:05:03 |       |       |
|   8 |         NESTED LOOPS                       |                            |   606 | 27876 |   325K  (1)| 01:05:03 |       |       |
|   9 |          VIEW                              |                            |  8168 |   103K|    19   (0)| 00:00:01 |       |       |
|  10 |           COLLECTION ITERATOR PICKLER FETCH|                            |  8168 | 16336 |    19   (0)| 00:00:01 |       |       |
|  11 |            FAST DUAL                       |                            |     1 |       |     2   (0)| 00:00:01 |       |       |
|  12 |          PARTITION RANGE MULTI-COLUMN      |                            |     1 |    33 |    40   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
|* 13 |           TABLE ACCESS BY LOCAL INDEX ROWID| AR1_CREDIT_DEBIT_LINK      |     1 |    33 |    40   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
|* 14 |            INDEX RANGE SCAN                | AR1_CREDIT_DEBIT_LINK_1IX  |     1 |       |    40   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
|* 15 |         TABLE ACCESS BY GLOBAL INDEX ROWID | AR1_CHARGE_GROUP           |     1 |    34 |     1   (0)| 00:00:01 | ROWID | ROWID |
|* 16 |          INDEX UNIQUE SCAN                 | AR1_CHARGE_GROUP_PK        |     1 |       |     1   (0)| 00:00:01 |       |       |
|  17 |        TABLE ACCESS BY GLOBAL INDEX ROWID  | AR1_INVOICE                |     1 |    50 |     1   (0)| 00:00:01 | ROWID | ROWID |
|* 18 |         INDEX UNIQUE SCAN                  | AR1_INVOICE_PK             |     1 |       |     1   (0)| 00:00:01 |       |       |
|* 19 |       INDEX UNIQUE SCAN                    | AR1_BILLING_ARRANGEMENT_PK |     1 |       |     1   (0)| 00:00:01 |       |       |
|  20 |      TABLE ACCESS BY INDEX ROWID           | AR1_BILLING_ARRANGEMENT    |     1 |    15 |     1   (0)| 00:00:01 |       |       |
|  21 |    HASH GROUP BY                           |                            |     1 |    39 | 43675   (1)| 00:08:45 |       |       |
|* 22 |     HASH JOIN                              |                            |     1 |    39 | 43673   (1)| 00:08:45 |       |       |
|  23 |      VIEW                                  |                            |  8168 |   103K|    19   (0)| 00:00:01 |       |       |
|  24 |       COLLECTION ITERATOR PICKLER FETCH    |                            |  8168 | 16336 |    19   (0)| 00:00:01 |       |       |
|  25 |        FAST DUAL                           |                            |     1 |       |     2   (0)| 00:00:01 |       |       |
|  26 |      PARTITION RANGE MULTI-COLUMN          |                            |  3191 | 82966 | 43654   (1)| 00:08:44 |KEY(MC)|KEY(MC)|
|* 27 |       TABLE ACCESS FULL                    | AR1_UNAPPLIED_CREDIT       |  3191 | 82966 | 43654   (1)| 00:08:44 |KEY(MC)|KEY(MC)|
-----------------------------------------------------------------------------------------------------------------------------------------
而性能瓶颈就在于一个全表扫描。

对于这条语句来说,从执行计划来看,在第24行出现了一个操作是COLLECTION ITERATOR PICKLER FETCH,相对比较陌生,查看了下,是对一个集合对象中的成员进行迭代取值,而这种操作在OTN中查看,被有些人评价为很糟糕的一种实现。
THE ABSOLUTELY WORSE THING (other than an ORA-00600 or ORA-3113) that you can see. 参见https://community.oracle.com/thread/1009301?tstart=0
哲学中说存在即合理,肯定是在特定的场景中使用才有一定的意义,主要在xml type的场景中会有所应用。这个场景肯定是不相关的。
我们把问题进行简化,即排除其它的Union 子查询过滤,定位到其中的一个子查询,因为只有这个子查询使用到了AR1_UNAPPLIED_CREDIT 这个表。
我们来看看这个子查询的执行计划情况。
SELECT /*+ leading (ar1_creditid_tab ar1_unapplied_credit) use_nl (ar1_creditid_tab ar1_unapplied_credit) */
           UNIQUE
           0,
           MAX (uc.credit_id) credit_id,
           0,
           0,
           0,
           SUM (uc.unapplied_amount) allocated_amount,
           TO_DATE ('') due_date,
           'Unapplied',
           '0',
           transaction_id
    FROM   ar1_unapplied_credit uc,
           (SELECT   COLUMN_VALUE AS credit_id
              FROM   table(SELECT   CAST (:5 AS ar1_numberarray_tp) credit_id
                             FROM   DUAL)) ar1_creditid_tab
   WHERE       uc.reversal_trans_id IS NULL
           AND uc.credit_id = ar1_creditid_tab.credit_id
           AND uc.partition_id = NVL (:6, 0)
           AND uc.credit_type LIKE :7
GROUP BY   uc.transaction_id

执行计划如下,可见访问路径能够复现。
Plan hash value: 981834188
-----------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name                 | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |                      |     1 |    39 | 43674   (1)| 00:08:45 |       |       |
|   1 |  HASH GROUP BY                       |                      |     1 |    39 | 43674   (1)| 00:08:45 |       |       |
|*  2 |   HASH JOIN                          |                      |     1 |    39 | 43673   (1)| 00:08:45 |       |       |
|   3 |    VIEW                              |                      |  8168 |   103K|    19   (0)| 00:00:01 |       |       |
|   4 |     COLLECTION ITERATOR PICKLER FETCH|                      |  8168 | 16336 |    19   (0)| 00:00:01 |       |       |
|   5 |      FAST DUAL                       |                      |     1 |       |     2   (0)| 00:00:01 |       |       |
|   6 |    PARTITION RANGE MULTI-COLUMN      |                      |  3191 | 82966 | 43654   (1)| 00:08:44 |KEY(MC)|KEY(MC)|
|*  7 |     TABLE ACCESS FULL                | AR1_UNAPPLIED_CREDIT |  3191 | 82966 | 43654   (1)| 00:08:44 |KEY(MC)|KEY(MC)|
-----------------------------------------------------------------------------------------------------------------------------

细看这条sql语句,其中有一个子查询有些陌生,使用到了嵌套表。
     (SELECT   COLUMN_VALUE AS credit_id
              FROM   table(SELECT   CAST (:5 AS ar1_numberarray_tp) credit_id
                             FROM   DUAL)) ar1_creditid_tab
对于这方面,自己也想开发讨教了下。大概知道了原委。
首先定义的type是number类型。
SQL> desc ar1_numberarray_tp
 ar1_numberarray_tp TABLE OF NUMBER
然后可以嵌入多个值,比如我们类似向数组传入100,200,用sql语句就是下面的形式,得到的结果还是type
SQL> SELECT   CAST (ar1_numberarray_tp(100,200) AS ar1_numberarray_tp) credit_id
                                 FROM   DUAL;
AR1_NUMBERARRAY_TP(100, 200)
这个时候结合起来,就得到了一个结果集。
SQL> SELECT   COLUMN_VALUE AS credit_id
              FROM   table(SELECT   CAST (ar1_numberarray_tp(100,200) AS ar1_numberarray_tp) credit_id
                                 FROM   DUAL);
       100
       200

明白了这点,就能基本定位问题了,看来这条sql语句功能还是传入对应的id,做了一个类似的行列转换
这个时候如果再能够进行简化。

SELECT   COLUMN_VALUE AS credit_id
              FROM   table(SELECT   CAST (ar1_numberarray_tp(100,200) AS ar1_numberarray_tp) credit_id
                                 FROM   DUAL);
简化为:
  (SELECT   :1 as credit_id from dual )
性能如何呢?
看看执行计划,可以看到资源消耗极低。比预想中要好得多。
-----------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                  | Name                       | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                           |                            |     2 |   158 |    60  (25)| 00:00:01 |       |       |
|   1 |  SORT UNIQUE                               |                            |     2 |   158 |    60  (25)| 00:00:01 |       |       |
|   2 |   UNION-ALL                                |                            |       |       |            |          |       |       |
|   3 |    HASH GROUP BY                           |                            |     1 |   132 |    47   (5)| 00:00:01 |       |       |
|   4 |     NESTED LOOPS                           |                            |       |       |            |          |       |       |
|   5 |      NESTED LOOPS                          |                            |     1 |   132 |    45   (0)| 00:00:01 |       |       |
|   6 |       NESTED LOOPS                         |                            |     1 |   117 |    44   (0)| 00:00:01 |       |       |
|   7 |        NESTED LOOPS                        |                            |     1 |    67 |    43   (0)| 00:00:01 |       |       |
|   8 |         NESTED LOOPS                       |                            |     1 |    33 |    42   (0)| 00:00:01 |       |       |
|   9 |          FAST DUAL                         |                            |     1 |       |     2   (0)| 00:00:01 |       |       |
|  10 |          PARTITION RANGE MULTI-COLUMN      |                            |     1 |    33 |    40   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
|* 11 |           TABLE ACCESS BY LOCAL INDEX ROWID| AR1_CREDIT_DEBIT_LINK      |     1 |    33 |    40   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
|* 12 |            INDEX RANGE SCAN                | AR1_CREDIT_DEBIT_LINK_1IX  |     1 |       |    40   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
|* 13 |         TABLE ACCESS BY GLOBAL INDEX ROWID | AR1_CHARGE_GROUP           |     1 |    34 |     1   (0)| 00:00:01 | ROWID | ROWID |
|* 14 |          INDEX UNIQUE SCAN                 | AR1_CHARGE_GROUP_PK        |     1 |       |     1   (0)| 00:00:01 |       |       |
|  15 |        TABLE ACCESS BY GLOBAL INDEX ROWID  | AR1_INVOICE                |     1 |    50 |     1   (0)| 00:00:01 | ROWID | ROWID |
|* 16 |         INDEX UNIQUE SCAN                  | AR1_INVOICE_PK             |     1 |       |     1   (0)| 00:00:01 |       |       |
|* 17 |       INDEX UNIQUE SCAN                    | AR1_BILLING_ARRANGEMENT_PK |     1 |       |     1   (0)| 00:00:01 |       |       |
|  18 |      TABLE ACCESS BY INDEX ROWID           | AR1_BILLING_ARRANGEMENT    |     1 |    15 |     1   (0)| 00:00:01 |       |       |
|  19 |    HASH GROUP BY                           |                            |     1 |    26 |    12  (17)| 00:00:01 |       |       |
|  20 |     NESTED LOOPS                           |                            |     1 |    26 |    10   (0)| 00:00:01 |       |       |
|  21 |      FAST DUAL                             |                            |     1 |       |     2   (0)| 00:00:01 |       |       |
|  22 |      PARTITION RANGE MULTI-COLUMN          |                            |     1 |    26 |     8   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
|* 23 |       TABLE ACCESS BY LOCAL INDEX ROWID    | AR1_UNAPPLIED_CREDIT       |     1 |    26 |     8   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
|* 24 |        INDEX RANGE SCAN                    | AR1_UNAPPLIED_CREDIT_1IX   |     1 |       |     8   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
-----------------------------------------------------------------------------------------------------------------------------------------

和开发进一步沟通,得到的反馈是可以从业务上进行简化和改造。
可以把原来的
SELECT   COLUMN_VALUE AS credit_id
              FROM   table(SELECT   CAST (ar1_numberarray_tp(100,200) AS ar1_numberarray_tp) credit_id
                                 FROM   DUAL);
改进为:
(select CREDIT_ID from ar1_payment WHERE ACCOUNT_ID = :1)

有了这些基础保证,再来看看整个sql语句的执行计划。
Plan hash value: 416684901
-----------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                  | Name                       | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                           |                            |    11 |   524 |   254  (49)| 00:00:04 |       |       |
|   1 |  SORT UNIQUE                               |                            |    11 |   524 |   254  (49)| 00:00:04 |       |       |
|   2 |   UNION-ALL                                |                            |       |       |            |          |       |       |
|   3 |    HASH GROUP BY                           |                            |     1 |   144 |   133   (2)| 00:00:02 |       |       |
|   4 |     NESTED LOOPS                           |                            |       |       |            |          |       |       |
|   5 |      NESTED LOOPS                          |                            |     1 |   144 |   131   (0)| 00:00:02 |       |       |
|   6 |       NESTED LOOPS                         |                            |     1 |   129 |   130   (0)| 00:00:02 |       |       |
|   7 |        NESTED LOOPS                        |                            |     1 |    79 |   129   (0)| 00:00:02 |       |       |
|   8 |         NESTED LOOPS                       |                            |     3 |   135 |   128   (0)| 00:00:02 |       |       |
|   9 |          PARTITION RANGE ALL               |                            |     3 |    36 |     9   (0)| 00:00:01 |     1 |    41 |
|  10 |           TABLE ACCESS BY LOCAL INDEX ROWID| AR1_CUSTOMER_CREDIT        |     3 |    36 |     9   (0)| 00:00:01 |     1 |    41 |
|* 11 |            INDEX RANGE SCAN                | AR1_CUSTOMER_CREDIT_3IX    |     3 |       |     8   (0)| 00:00:01 |     1 |    41 |
|  12 |          PARTITION RANGE MULTI-COLUMN      |                            |     1 |    33 |    40   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
|* 13 |           TABLE ACCESS BY LOCAL INDEX ROWID| AR1_CREDIT_DEBIT_LINK      |     1 |    33 |    40   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
|* 14 |            INDEX RANGE SCAN                | AR1_CREDIT_DEBIT_LINK_1IX  |     1 |       |    40   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
|* 15 |         TABLE ACCESS BY GLOBAL INDEX ROWID | AR1_CHARGE_GROUP           |     1 |    34 |     1   (0)| 00:00:01 | ROWID | ROWID |
|* 16 |          INDEX UNIQUE SCAN                 | AR1_CHARGE_GROUP_PK        |     1 |       |     1   (0)| 00:00:01 |       |       |
|  17 |        TABLE ACCESS BY GLOBAL INDEX ROWID  | AR1_INVOICE                |     1 |    50 |     1   (0)| 00:00:01 | ROWID | ROWID |
|* 18 |         INDEX UNIQUE SCAN                  | AR1_INVOICE_PK             |     1 |       |     1   (0)| 00:00:01 |       |       |
|* 19 |       INDEX UNIQUE SCAN                    | AR1_BILLING_ARRANGEMENT_PK |     1 |       |     1   (0)| 00:00:01 |       |       |
|  20 |      TABLE ACCESS BY INDEX ROWID           | AR1_BILLING_ARRANGEMENT    |     1 |    15 |     1   (0)| 00:00:01 |       |       |
|  21 |    HASH GROUP BY                           |                            |    10 |   380 |   121   (2)| 00:00:02 |       |       |
|  22 |     NESTED LOOPS                           |                            |       |       |            |          |       |       |
|  23 |      NESTED LOOPS                          |                            |    10 |   380 |   119   (0)| 00:00:02 |       |       |
|  24 |       PARTITION RANGE ALL                  |                            |    10 |   120 |    41   (0)| 00:00:01 |     1 |   201 |
|  25 |        TABLE ACCESS BY LOCAL INDEX ROWID   | AR1_PAYMENT                |    10 |   120 |    41   (0)| 00:00:01 |     1 |   201 |
|* 26 |         INDEX RANGE SCAN                   | AR1_PAYMENT_1IX            |    10 |       |    40   (0)| 00:00:01 |     1 |   201 |
|  27 |       PARTITION RANGE MULTI-COLUMN         |                            |     1 |       |     8   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
|* 28 |        INDEX RANGE SCAN                    | AR1_UNAPPLIED_CREDIT_1IX   |     1 |       |     8   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
|* 29 |      TABLE ACCESS BY LOCAL INDEX ROWID     | AR1_UNAPPLIED_CREDIT       |     1 |    26 |     8   (0)| 00:00:01 |     1 |     1 |
-----------------------------------------------------------------------------------------------------------------------------------------

可以看到性能的提升是非常大的。
通过这个案例,我们可以看到,对于sql调优的很多关键点还是需要和开发配合,从业务上进行支持是很快捷的一种方式。这种调优方式可以从整体的角度来看待这个问题,而不单单是技术角度。这个时候调优工作就会轻松不少,清晰不少。
在定位sql语句的性能瓶颈时,发现全表扫描相关的COLLECTION ITERATOR PICKLER FETCH操作在这个场景中是不合适的。能够用相关的索引扫描或者临时表来代替都是不错的选择。

时间: 2024-10-12 22:57:37

执行计划中的COLLECTION ITERATOR PICKLER FETCH导致的性能问题的相关文章

MongoDB执行计划获取(db.collection.explain())

在RDBMS中,无论那种数据库,都提供了SQL剖析工具,用来解决SQL效率低下的问题.在MongoDB中,也有相应的策略来实现剖析.MongoDB提供了db.collection.explain()方法, cursor.explain()方法,和explain命令去返回查询计划信息和查询计划的执行统计信息.这为我们诊断查询提供了极大的便利,本文主要描述db.collection.explain()的相关用法. 一.db.collection.explain()简介 支持下列操作返回查询计划 ag

关于执行计划中的%CPU的含义

今天突然想起前段时间学习的一篇博客,是oaktable的Charles Hooper所写,链接为: https://hoopercharles.wordpress.com/2010/02/19/what-is-the-meaning-of-the-cpu-column-in-an-explain-plan/ 自己也趁机消化了一下.对于执行计划中的 列Cost (%CPU),其中的%CPU的含义很少有人能够说得清楚,于是Charles Hooper写了上面的文章来解释. 对于执行计划的信息都会放入

通过执行计划中的CONCATENATION分析sql问题

昨天开发的一个同事找到我,说写了一条sql语句,但是执行了半个小时还没有执行完,想让我帮忙看看是怎么回事. 他大体上给我讲了下逻辑,表bl1_rc_rates是千万级数据量的表,autsu_subscriber 是个临时表,里面只有三百多条数据,bl1_activity_history 表的数据量略小,是百万级的.    select distinct hist.entity_id, rc.* from bl1_activity_history hist, bl1_rc_rates rc, au

浅析SQL SERVER执行计划中的各类怪相

在查看执行计划或调优过程中,执行计划里面有些现象总会让人有些疑惑不解:     1:为什么同一条SQL语句有时候会走索引查找,有时候SQL脚本又不走索引查找,反而走全表扫描?     2:同一条SQL语句,查询条件的取值不同,它的执行计划会一致吗?     3: 同一条SQL语句,其执行计划会变化,为什么     4: 在查询条件的某个或几个字段上创建了索引,执行计划就一定会走该索引吗?     5:同时存在几个索引,SQL语句会走那个索引?      .....................

《Oracle高性能SQL引擎剖析:SQL优化与调优机制详解》一2.5 执行计划中其他信息的含义

2.5 执行计划中其他信息的含义 通过DBMS_XPLAN输出执行计划,除了计划本身外,还可以获得一些其他信息帮助我们进一步分析执行计划及语句性能. 2.5.1 查询块和对象别名 在使用DBMS_XPLAN显示执行计划时,选择'ADVANCED'预定义格式作为参数或者加入'ALIAS'控制字符串,可以在输出中看到以下内容: Query Block Name / Object Alias (identified by operation id): -------------------------

执行计划中常见index访问方式(转)

近期有朋友对于单个表上的index各种情况比较模糊,这里对于单个表上,单个index出现的大多数情况进行了总结性测试,给出了测试结果,至于为什么出现这样的试验结果未做过多解释,给读者留下思考的空间.本篇文章仅仅是为了测试hint对index的影响,而不是说明走各种index方式的好坏.参考: INDEX FULL SCAN vs INDEX FAST FULL SCAN创建表模拟测试 SQL> create table t_xifenfei as select object_id,object_

执行计划中各字段各模块描述

      在SQL语句的执行计划中,包含很多字段项和很多模块,其不同字段代表了不同的含义且在不同的情形下某些字段.模块显示或不显示,下面的描述给出了执行计划中各字段的含义以及各模块的描述.        有关执行计划中各字段模块的描述请参考: 执行计划中各字段各模块描述        有关由SQL语句来获取执行计划请参考:     使用 EXPLAIN PLAN 获取SQL语句执行计划        有关使用autotrace来获取执行计划请参考:启用 AUTOTRACE 功能       有

FAQ系列 | EXPLAIN执行计划中要重点关注哪些要素

导读 EXPLAIN的结果中,有哪些关键信息值得注意呢? MySQL的EXPLAIN当然和ORACLE的没法比,不过我们从它输出的结果中,也可以得到很多有用的信息. 总的来说,我们只需要关注结果中的几列: 列名 备注 type 本次查询表联接类型,从这里可以看到本次查询大概的效率 key 最终选择的索引,如果没有索引的话,本次查询效率通常很差 key_len 本次查询用于结果过滤的索引实际长度,参见另一篇分享(FAQ系列-解读EXPLAIN执行计划中的key_len) rows 预计需要扫描的记

FAQ系列 | 解读EXPLAIN执行计划中的key_len

导读 EXPLAIN中的key_len一列表示什么意思,该如何解读? EXPLAIN执行计划中有一列 key_len 用于表示本次查询中,所选择的索引长度有多少字节,通常我们可借此判断联合索引有多少列被选择了. 在这里 key_len 大小的计算规则是: 一般地,key_len 等于索引列类型字节长度,例如int类型为4-bytes,bigint为8-bytes: 如果是字符串类型,还需要同时考虑字符集因素,例如:CHAR(30) UTF8则key_len至少是90-bytes: 若该列类型定义