执行计划中与分区相关的OP介绍

来源:互联网 发布:js查找最大值 编辑:程序博客网 时间:2024/05/01 16:48
在公司整理业务库性能不佳的SQL语句时碰到了较多对分区表的查询语句,在执行计划中发现很多语句都对整个分区表进行扫描,而且大部分还是走的TAF(Table Access Full),因此,这里就对分区表操作时会出现的一些执行计划OP做个介绍


[oracle@ora11g ~]$ sqlplus / as sysdba


SQL*Plus: Release 11.2.0.4.0 Production on Sat Oct 31 22:44:31 2015


Copyright (c) 1982, 2013, Oracle.  All rights reserved.


Connected to an idle instance.


SQL> startup
ORACLE instance started.


Total System Global Area  835104768 bytes
Fixed Size    2257840 bytes
Variable Size  507513936 bytes
Database Buffers  322961408 bytes
Redo Buffers    2371584 bytes
Database mounted.
Database opened.


--创建测试分区表zlm
SQL> create table zlm partition by range(object_id)
 (partition p1 values less than(15000),
  partition p2 values less than(30000),
  partition p3 values less than(45000),
  partition p4 values less than(60000),
  partition p_max values less than(maxvalue))
  as select * from dba_objects;  2    3    4    5    6    7  


Table created.


SQL> select count(*) from zlm;


  COUNT(*)
----------
     86978
 
SQL> select object_id,object_type from zlm where object_name='ZLM';


 OBJECT_ID OBJECT_TYPE
---------- -------------------
     88862 TABLE PARTITION
     88863 TABLE PARTITION
     88861 TABLE PARTITION
     88860 TABLE PARTITION
     88859 TABLE PARTITION
     88858 TABLE


6 rows selected.


--在分区表上创建本地分区索引
SQL> create index ind_zlm_o_name on zlm(object_name) local;


Index created.


SQL> set autot trace exp
SQL> select * from zlm where object_name='ZLM';


Execution Plan
----------------------------------------------------------
Plan hash value: 1634303508


---------------------------------------------------------------------------------------------------------------------
| Id  | Operation   | Name   | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |   | 6 |  1242 |10   (0)| 00:00:01 |   |   |
|   1 |  PARTITION RANGE ALL  |   | 6 |  1242 |10   (0)| 00:00:01 | 1 | 5 |
|   2 |   TABLE ACCESS BY LOCAL INDEX ROWID| ZLM    |  6 |  1242 |10   (0)| 00:00:01 | 1 | 5 |
|*  3 |    INDEX RANGE SCAN   | IND_ZLM_O_NAME | 6 |   | 6   (0)| 00:00:01 | 1 | 5 |
---------------------------------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------


   3 - access("OBJECT_NAME"='ZLM')


Note
-----
   - dynamic sampling used for this statement (level=2)


PARTITION RANGE ALL:
表示扫描所有分区,此处是第1-5个分区都被扫描了一遍,才得到查询结果。由于在object_name列上有一个本地分区索引,并且是非唯一记录,因此还走了一个IRS。
   
SQL> select * from zlm where object_id=18000;


Execution Plan
----------------------------------------------------------
Plan hash value: 636735399


-----------------------------------------------------------------------------------------------
| Id  | Operation       | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |      |     2 |   414 |    61(0)| 00:00:01 |       |       |
|   1 |  PARTITION RANGE SINGLE|      |     2 |   414 |    61(0)| 00:00:01 |     2 |     2 |
|*  2 |   TABLE ACCESS FULL    | ZLM  |     2 |   414 |    61(0)| 00:00:01 |     2 |     2 |
-----------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------


   2 - filter("OBJECT_ID"=18000)


Note
-----
   - dynamic sampling used for this statement (level=2)
   
PARTITION RANGE SINGLE:
表示只扫描单个分区,这里Pstart和Pstop的值都为2,说明只对第2个分区进行了扫描,因为oracle的cbo会根据where过滤条件中的分区键值判断只扫描需要访问的分区,对于不需要的分区不会访问,这个特性叫做partition purging。由于object_id上未建立分区索引,因此只能走TAF。


SQL> select * from zlm where object_id=88858 and object_name='ZLM';


Execution Plan
----------------------------------------------------------
Plan hash value: 480976975


---------------------------------------------------------------------------------------------------------------------
| Id  | Operation   | Name   | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |   | 3 |621 | 4   (0)| 00:00:01 |   |   |
|   1 |  PARTITION RANGE SINGLE   |   | 3 |621 | 4   (0)| 00:00:01 | 5 | 5 |
|*  2 |   TABLE ACCESS BY LOCAL INDEX ROWID| ZLM    |  3 | 621 |  4   (0)| 00:00:01 | 5 | 5 |
|*  3 |    INDEX RANGE SCAN   | IND_ZLM_O_NAME | 4 |   | 1   (0)| 00:00:01 | 5 | 5 |
---------------------------------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------


   2 - filter("OBJECT_ID"=88858)
   3 - access("OBJECT_NAME"='ZLM')


Note
-----
   - dynamic sampling used for this statement (level=2)


同样地,当指定的object_id落在第5个分区范围之内时,也只扫描了第5个分区。由于where条件中的object_name列上有索引,所以这次就不走TAF了,而是走IRS。      
SQL> select * from zlm where object_id in (4000,8000,16000,32000);


Execution Plan
----------------------------------------------------------
Plan hash value: 419480214


-----------------------------------------------------------------------------------------------
| Id  | Operation       | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |      |    10 |  2070 |   176(2)| 00:00:03 |       |       |
|   1 |  PARTITION RANGE INLIST|      |    10 |  2070 |   176(2)| 00:00:03 |KEY(I) |KEY(I) |
|*  2 |   TABLE ACCESS FULL    | ZLM  |    10 |  2070 |   176(2)| 00:00:03 |KEY(I) |KEY(I) |
-----------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------


   2 - filter("OBJECT_ID"=4000 OR "OBJECT_ID"=8000 OR "OBJECT_ID"=16000 OR
     "OBJECT_ID"=32000)


Note
-----
   - dynamic sampling used for this statement (level=2)


PARTITION RANGE INLIST:
当分区关键字存在多个可选值时,比较明显的是查询中有in(n1,n2,n3...)和par_key=n1 or par_key=n2。注意,这里的执行计划走的是PARTITION RANGE INLIST,而且Pstart和Pstop都是用的KEY(I),不过这并不代表只扫描了一个分区,而是扫描了3个分区,其中4000和8000属于第1个分区,16000属于第2个分区,32000属于第3个分区。
   
SQL> select * from zlm where object_id in (8000);


Execution Plan
----------------------------------------------------------
Plan hash value: 636735399


-----------------------------------------------------------------------------------------------
| Id  | Operation       | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |      |     2 |   414 |    53(0)| 00:00:01 |       |       |
|   1 |  PARTITION RANGE SINGLE|      |     2 |   414 |    53(0)| 00:00:01 |     1 |     1 |
|*  2 |   TABLE ACCESS FULL    | ZLM  |     2 |   414 |    53(0)| 00:00:01 |     1 |     1 |
-----------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------


   2 - filter("OBJECT_ID"=8000)


Note
-----
   - dynamic sampling used for this statement (level=2)


注意,以上尽管是inlist格式的查询,但只给了一个值,因此依旧出现了PARTITION RANGE SINGLE


SQL> select * from zlm where object_id>15000;


Execution Plan
----------------------------------------------------------
Plan hash value: 2128087405


-------------------------------------------------------------------------------------------------
| Id  | Operation | Name | Rows| Bytes | Cost (%CPU)| Time| Pstart| Pstop |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT || 84438 |    16M|   295   (4)| 00:00:04 |||
|   1 |  PARTITION RANGE ITERATOR|| 84438 |    16M|   295   (4)| 00:00:04 |     2 |     5 |
|*  2 |   TABLE ACCESS FULL | ZLM| 84438 |    16M|   295   (4)| 00:00:04 |     2 |     5 |
-------------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------


   2 - filter("OBJECT_ID">15000)


Note
-----
   - dynamic sampling used for this statement (level=2)


PARTITION RANGE ITERATOR:
表示多个分区迭代扫描,当cbo需要扫描多个连续的分区时就会出现,Pstart和Pstop为需要扫描的分区,此处扫描的分区为2-5,一共扫描了4个分区。同样,由于这里where条件中的object_id上没有索引,因此走了TAF。


SQL> select * from zlm where object_id<15000;


Execution Plan
----------------------------------------------------------
Plan hash value: 636735399


-----------------------------------------------------------------------------------------------
| Id  | Operation       | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |      | 13285 |  2685K|    53(0)| 00:00:01 |       |       |
|   1 |  PARTITION RANGE SINGLE|      | 13285 |  2685K|    53(0)| 00:00:01 |     1 |     1 |
|   2 |   TABLE ACCESS FULL    | ZLM  | 13285 |  2685K|    53(0)| 00:00:01 |     1 |     1 |
-----------------------------------------------------------------------------------------------


Note
-----
   - dynamic sampling used for this statement (level=2)


一旦需要查询的记录落在单一的分区内,PARTITION RANGE ITERATOR就会又变为PARTITION RANGE SINGLE,此处只需扫描分区1就可以满足查询需求了。


SQL> select * from zlm where object_id<8000 and object_id>32000;


Execution Plan
----------------------------------------------------------
Plan hash value: 3446828199


-----------------------------------------------------------------------------------------------
| Id  | Operation       | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |      |     1 |   207 |     0(0)|     |       |       |
|*  1 |  FILTER       |      |       |       |  |     |       |       |
|   2 |   PARTITION RANGE EMPTY|      |     1 |   207 |    53(0)| 00:00:01 |INVALID|INVALID|
|*  3 |    TABLE ACCESS FULL   | ZLM  |     1 |   207 |    53(0)| 00:00:01 |INVALID|INVALID|
-----------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------


   1 - filter(NULL IS NOT NULL)
   3 - filter("OBJECT_ID"<8000 AND "OBJECT_ID">32000)


Note
-----
   - dynamic sampling used for this statement (level=2)


PARTITION RANGE EMPTY:
表示cbo根据统计信息和sql语句,得出该表所对应的分区不存在任何符合查询的结果集。通常在SQL语句的业务逻辑出现问题时产生,因为同时满足object id既小于8000又大于18000的记录是不存在的。


--创建分区表zlm2
SQL> create table zlm2 partition by range(object_id)
  2   (partition p1 values less than(15000),
  partition p2 values less than(30000),
  partition p3 values less than(45000),
  partition p4 values less than(60000))
 as select * from dba_objects where object_id<60000;  3    4    5    6  


Table created.


SQL> select * from zlm2 where object_id>=60000;


Execution Plan
----------------------------------------------------------
Plan hash value: 2767703983


----------------------------------------------------------------------------------------------
| Id  | Operation      | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |      |   1 |207 | 53   (0)| 00:00:01 |    |    |
|   1 |  PARTITION RANGE EMPTY|      |   1 |207 | 53   (0)| 00:00:01 |INVALID|INVALID|
|*  2 |   TABLE ACCESS FULL   | ZLM2 |   1 |207 | 53   (0)| 00:00:01 |INVALID|INVALID|
----------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------


   2 - filter("OBJECT_ID">=60000)


Note
-----
   - dynamic sampling used for this statement (level=2)


显然,此时对于查询object id大于等于60000的记录尽管没有逻辑上的错误,但是由于在创建分区表zlm2时并未将这部分数据包含在分区表内,对于该分区表而言,所查询的数据依然是不存在的,因此依然会出现PARTITION RANGE EMPTY,Pstart和Pstop的值也都是INVALID的


那么我们把之前的那条不符合逻辑的SQL查询语句中的and换成or又会如何呢?


SQL> select * from zlm where object_id<8000 or object_id>32000;


Execution Plan
----------------------------------------------------------
Plan hash value: 2323138327


-------------------------------------------------------------------------------------------
| Id  | Operation   | Name | Rows  | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |  | 58782 |    11M|   287   (3)| 00:00:04 | | |
|   1 |  PARTITION RANGE OR| | 58782 |    11M|   287   (3)| 00:00:04 |KEY(OR)|KEY(OR)|
|*  2 |   TABLE ACCESS FULL| ZLM  | 58782 |    11M|   287   (3)| 00:00:04 |KEY(OR)|KEY(OR)|
-------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------


   2 - filter("OBJECT_ID"<8000 OR "OBJECT_ID">32000)


Note
-----
   - dynamic sampling used for this statement (level=2)


SQL> 
   
PARTITION RANGE OR:
表示where条件中存在了or运算,并且cbo得出这些运算需要跨越多个不连续的分区,Pstart| Pstop中出现了KEY(OR)的值,它应该跟PARTITION RANGE INLIST中的key(I)一样,也是一个变化的值,所以这里扫描的分区数量具有不确切性。


以上就是对于在访问分区表进行查询时,可能在执行计划中出现的各种OP操作,希望对大家在分析分区表的执行计划时能带来一些帮助。
  
   
   
   

0 0
原创粉丝点击