分区扫描执行计划分析简介

SQL> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - 64bi
PL/SQL Release 10.2.0.5.0 - Production
CORE    10.2.0.5.0      Production
TNS for Linux: Version 10.2.0.5.0 - Production
NLSRTL Version 10.2.0.5.0 - Production

SQL> create table tab01 partition by range(object_id)
  2  (partition p1 values less than(10000),
  3  partition p2 values less than(20000),
  4  partition p3 values less than(30000),
  5  partition p_max values less than(maxvalue))
  6  as select * from dba_objects;

SQL> create index index_tab01_name on tab01(object_name) local;

PARTITION RANGE ALL是所有分区扫描,比如需要扫描所有的分区才能满足查询
SQL> select * from tab01 where object_name=‘TT‘;

Execution Plan
----------------------------------------------------------
Plan hash value: 3187188282

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

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

3 - access("OBJECT_NAME"=‘TT‘)

我们观察到执行计划中跟非分区表多出了pstart和pstop这两项,这个表示扫描该表的所有分区,也就是分区:1到分区:4.

Cbo下的分区还有一个特别重要的特性:cbo会根据where过滤条件中的分区键值判断只扫描需要访问的分区,对于不需要的分区不会访问,这个特性叫做partition purging
PARTITION RANGE SINGLE是单个分区扫描,比如查询中可以看见很清楚的过滤条件表示只需要扫描某一个分区即可
SQL>  select * from tab01 where object_id=5000;

Execution Plan
----------------------------------------------------------
Plan hash value: 2393410319

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

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

2 - filter("OBJECT_ID"=5000)

SQL>  select * from tab01 where object_id=5000 and object_name=‘OP‘;

Execution Plan
----------------------------------------------------------
Plan hash value: 3913001591

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

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

2 - filter("OBJECT_ID"=5000)
   3 - access("OBJECT_NAME"=‘OP‘)

这里的pstart和pstop都是2,表示只扫描了一个分区。

PARTITION RANGE INLIST:当分区关键字存在多个可选值时,比较明显的是查询中有in(n1,n2 。。。nmax)和par_key=n1 or par_key=n2,注意单个可选值并不会出现PARTITION RANGE INLIST而是出现partition range  single
SQL> select * from tab01 where object_id in (5000,5500);

Execution Plan
----------------------------------------------------------
Plan hash value: 2325940683

------------------------------------------------------------------------------------------------
| Id  | Operation              | Name  | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |       |     2 |   172 |    57   (2)| 00:00:01 |       |       |
|   1 |  PARTITION RANGE INLIST|       |     2 |   172 |    57   (2)| 00:00:01 |KEY(I) |KEY(I) |
|*  2 |   TABLE ACCESS FULL    | TAB01 |     2 |   172 |    57   (2)| 00:00:01 |KEY(I) |KEY(I) |
------------------------------------------------------------------------------------------------

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

2 - filter("OBJECT_ID"=5000 OR "OBJECT_ID"=5500)

注意这里的执行计划是partition range inlist,而且pstart和pstop都是用的key(I),不过这个并不代表只扫描了一个分区,如下:
SQL> select * from tab01 where object_id in (5000,20000);

Execution Plan
----------------------------------------------------------
Plan hash value: 2325940683

------------------------------------------------------------------------------------------------
| Id  | Operation              | Name  | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |       |     2 |   172 |    88   (2)| 00:00:02 |       |       |
|   1 |  PARTITION RANGE INLIST|       |     2 |   172 |    88   (2)| 00:00:02 |KEY(I) |KEY(I) |
|*  2 |   TABLE ACCESS FULL    | TAB01 |     2 |   172 |    88   (2)| 00:00:02 |KEY(I) |KEY(I) |
------------------------------------------------------------------------------------------------

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

2 - filter("OBJECT_ID"=5000 OR "OBJECT_ID"=20000)

该查询会去扫描两个分区,而这里还是partition range inlist ,pstart和pstop都是KEY(I),所以这里并不表示只扫描了一个分区,至于这个key(I)在官档中并没有找到一个确切的说明,小鱼觉得这个应该是个变量的形式,所以访问的分区当然也可能出现多个。

partition by iterator:分区迭代扫描,当cbo需要扫描多个连续的分区时就会出现partition by iterator分区迭代,pstart和pstop为需要扫描的分区
SQL> select * from tab01 where object_id>10000;

Execution Plan
----------------------------------------------------------
Plan hash value: 232993693

--------------------------------------------------------------------------------------------------
| Id  | Operation                | Name  | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |       | 40772 |  3424K|   153   (2)| 00:00:02 |       |       |
|   1 |  PARTITION RANGE ITERATOR|       | 40772 |  3424K|   153   (2)| 00:00:02 |     2 |     4 |
|*  2 |   TABLE ACCESS FULL      | TAB01 | 40772 |  3424K|   153   (2)| 00:00:02 |     2 |     4 |
--------------------------------------------------------------------------------------------------

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

2 - filter("OBJECT_ID">10000)

初步来看partition range iterator应该是多个分区的迭代扫描,而上述测试用例也是如此,扫描的分区为pstart:2到pstop :4,一共三个分区。

但是小鱼发现优化器在下列查询时也会采取partition range iterator,请看下例:
SQL>  select * from tab01 where object_id<5000;

Execution Plan
----------------------------------------------------------
Plan hash value: 232993693

--------------------------------------------------------------------------------------------------
| Id  | Operation                | Name  | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |       |  4660 |   391K|    60   (2)| 00:00:01 |       |       |
|   1 |  PARTITION RANGE ITERATOR|       |  4660 |   391K|    60   (2)| 00:00:01 |     1 |     2 |
|*  2 |   TABLE ACCESS FULL      | TAB01 |  4660 |   391K|    60   (2)| 00:00:01 |     1 |     2 |
--------------------------------------------------------------------------------------------------

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

2 - filter("OBJECT_ID"<5000)

按理说object_id<5000只需要扫描分区p1即可,而这里cbo确给出了partition range iterator执行计划,并且pstart为1,pstop为2,cbo根据统计信息得出需要扫描partition :1和:2两个分区。

这里小鱼想的是否能够通过动态采样让cbo选择只扫描一个分区,从而出现partition range single的执行计划,不过这里小鱼即使采取动态采样level 10,依然cbo给出的执行计划依然是partition range iterator,从这里看出cbo并不是绝对的智能,当统计信息越准确它给出的执行计划就越准,但是还是可能出现不完全执行计划。
SQL> select /*+dynamic_sampling(tab01 10)*/* from tab01 where object_id<2000;

Execution Plan
----------------------------------------------------------
Plan hash value: 232993693

--------------------------------------------------------------------------------------------------
| Id  | Operation                | Name  | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |       |  1953 |   164K|    60   (2)| 00:00:01 |       |       |
|   1 |  PARTITION RANGE ITERATOR|       |  1953 |   164K|    60   (2)| 00:00:01 |     1 |     2 |
|*  2 |   TABLE ACCESS FULL      | TAB01 |  1953 |   164K|    60   (2)| 00:00:01 |     1 |     2 |
--------------------------------------------------------------------------------------------------

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

2 - filter("OBJECT_ID"<2000)

Note
-----
   - dynamic sampling used for this statement

但是如果我们缩小查询的范围,果然就出现了我们想要的partition range single执行计划,而且pstart和pstop都是1,这里表示优化器确实只准备扫描一个分区。
SQL> select * from tab01 where object_id<200;

Execution Plan
----------------------------------------------------------
Plan hash value: 2393410319

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

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

2 - filter("OBJECT_ID"<200)

PARTITION RANGE EMPTY这个表示cbo根据统计信息和sql语句,得出该表所对应的分区不存在任何符合查询的结果集,就会显示partition range empty。

例如sql业务逻辑存在问题:
SQL> select * from tab01 where object_id<2000 and object_id>20000;

Execution Plan
----------------------------------------------------------
Plan hash value: 2001509460

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

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

1 - filter(NULL IS NOT NULL)
   3 - filter("OBJECT_ID"<2000 AND "OBJECT_ID">20000)

再例如指定的查询根据表的定义确定没有满足查询的分区:
SQL> create table tab02 partition by range(object_id)
  2      (partition p1 values less than(10000),
  3      partition p2 values less than(20000),
  4      partition p3 values less than(30000))
  5      as select * from dba_objects where object_id<30000;

Table created.

SQL> select * from tab02 where object_id>30000;

Execution Plan
----------------------------------------------------------
Plan hash value: 559767399

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

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

2 - filter("OBJECT_ID">30000)

Note
-----
   - dynamic sampling used for this statement

看出这里的pstart和pstop都是invalid的。

Partition range or:表示where条件中存在了or运算,并且cbo得出这些运算需要跨越多个不连续的分区时
SQL> select * from tab01 where object_id<5000 or (object_id>5000 and object_id<8000);

Execution Plan
----------------------------------------------------------
Plan hash value: 1971037408

--------------------------------------------------------------------------------------------
| Id  | Operation          | Name  | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |       |  7196 |   604K|    60   (2)| 00:00:01 |       |       |
|   1 |  PARTITION RANGE OR|       |  7196 |   604K|    60   (2)| 00:00:01 |KEY(OR)|KEY(OR)|
|*  2 |   TABLE ACCESS FULL| TAB01 |  7196 |   604K|    60   (2)| 00:00:01 |KEY(OR)|KEY(OR)|
--------------------------------------------------------------------------------------------

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

2 - filter("OBJECT_ID"<5000 OR "OBJECT_ID"<8000 AND "OBJECT_ID">5000)

可能有些朋友觉得这个其实只需要扫描一个分区即可,因为这个where object_id<5000 or (object_id>5000 and object_id<8000)过滤条件,但是cbo并不能如此的智能,此时cbo根据其统计信息和sql语句得出其需要计算扫描时可能需要跨越不连续的多个分区,当然也可能不需要,所以这里也自然也就出现了PARTITION RANGE OR了,而这个key(OR)应该跟partition range inlist中的key(I)一样,也是一个变化的值,所以这里扫描的分区数量具有不确切性。

同样动态采样也是如此:
SQL> select /*+dynamic_sampling(tab01 10)*/* from tab01 where object_id<5000 or (object_id>5000 and object_id<8000);

Execution Plan
----------------------------------------------------------
Plan hash value: 1971037408

--------------------------------------------------------------------------------------------
| Id  | Operation          | Name  | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |       |  7772 |   652K|    60   (2)| 00:00:01 |       |       |
|   1 |  PARTITION RANGE OR|       |  7772 |   652K|    60   (2)| 00:00:01 |KEY(OR)|KEY(OR)|
|*  2 |   TABLE ACCESS FULL| TAB01 |  7772 |   652K|    60   (2)| 00:00:01 |KEY(OR)|KEY(OR)|
--------------------------------------------------------------------------------------------

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

2 - filter("OBJECT_ID"<5000 OR "OBJECT_ID"<8000 AND "OBJECT_ID">5000)

Note
-----
   - dynamic sampling used for this statement

上面小鱼简单介绍了range分区扫描的几种执行路径,有些oracle的官档中都没有确切的说明,当然随着我们对cbo的认识,有些问题会随着反复学习和接触新的东西慢慢解开。

新年上班已经一周多了,渐渐也要抓紧了,这年任务还是很大的:ocm考试、深入oracle和常用的高可用、mysql和一些nosql、os的东西等,当然还有最头疼的english!

原文:http://www.dbaxiaoyu.com/archives/1780

时间: 2024-10-14 13:18:47

分区扫描执行计划分析简介的相关文章

MySQL学习系列2--MySQL执行计划分析EXPLAIN

1.Explain语法 EXPLAIN SELECT -- 变体:   EXPLAIN EXTENDED SELECT -- 将执行计划"反编译"成SELECT语句,运行SHOW WARNINGS 可得到被MySQL优化器优化后的查询语句 2.执行计划分析和实例 创建员工表Employee create table Employee ( ID int auto_increment, Ename varchar(32), Age int, Salary float, MID int, P

MongoDB执行计划分析详解(1)

正文 queryPlanner queryPlanner是现版本explain的默认模式,queryPlanner模式下并不会去真正进行query语句查询,而是针对query语句进行执行计划分析并选出winning plan. { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "game_db.game_user", "indexFilterSet&qu

mysql 执行计划分析三看, explain,profiling,optimizer_trace

http://blog.csdn.net/xj626852095/article/details/52767963 step 1 使用explain 查看执行计划, 5.6后可以加参数 explain format=json xxx 输出json格式的信息 step 2  使用profiling详细的列出在每一个步骤消耗的时间,前提是先执行一遍语句. #打开profiling 的设置 SET profiling = 1; SHOW VARIABLES LIKE '%profiling%'; #查

oracle sql 执行计划分析

转自http://itindex.net/detail/45962-oracle-sql-%E8%AE%A1%E5%88%92 一.首先创建表 SQL> show user USER is "RHYS" SQL> create table A(col1 number(4,0),col2 number(4,0), col4 char(30)); create table B(col1 number(4,0),col3 number(4,0), name_b char(30))

mysql执行计划分析

本实验employees数据库从https://launchpad.net/test-db/下载 1.id列 若一个select语句中要连接多个数据表,执行计划会根据连接的表数进行显示,并赋予相同的id.下面示例只有一个select语句,但连接多个表,此时id值不会增加,拥有相同id: root@localhost:mysql3316.sock 11:58:51 [employees]>explain extended select e.emp_no,e.first_name,s.from_da

【转】MySQL执行计划分析

原文:http://www.cnblogs.com/wangyanhong/archive/2013/09/18/3327919.html 一.语法explain <sql语句>例如: explain select * from t3 where id=3952602; 二.explain输出解释 +----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+

sqlplus 分析执行计划

转载 http://xm-koma.iteye.com/blog/1048451 对于oracle9i,需要手工设置plustrace角色,步骤如下: 1.在SQL>connect sys/密码 as sysdba  (密码为:数据库所在的那台服务器的密码) 在sys用户下运行$ORACLE_HOME/sqlplus/admin/plustrce.sql SQL>@$ORACLE_HOME/sqlplus/admin/plustrce.sql 这段sql的实际内容如下: set echo on

MSSQLSERVER执行计划详解

序言 本篇主要目的有二: 1.看懂t-sql的执行计划,明白执行计划中的一些常识. 2.能够分析执行计划,找到优化sql性能的思路或方案. 如果你对sql查询优化的理解或常识不是很深入,那么推荐几骗博文给你:SqlServer性能检测和优化工具使用详细 ,sql语句的优化分析,T-sql语句查询执行顺序. 执行计划简介 1.什么是执行计划? 大哥提交的sql语句,数据库查询优化器,经过分析生成多个数据库可以识别的高效执行查询方式.然后优化器会在众多执行计划中找出一个资源使用最少,而不是最快的执行

在MySQL中使用explain查询SQL的执行计划

1.什么是MySQL执行计划 要对执行计划有个比较好的理解,需要先对MySQL的基础结构及查询基本原理有简单的了解. MySQL本身的功能架构分为三个部分,分别是 应用层.逻辑层.物理层,不只是MySQL ,其他大多数数据库产品都是按这种架构来进行划分的. 应用层,主要负责与客户端进行交互,建立链接,记住链接状态,返回数据,响应请求,这一层是和客户端打交道的. 逻辑层,主要负责查询处理.事务管理等其他数据库功能处理,以查询为例. 首先接收到查询SQL之后,数据库会立即分配一个线程对其进行处理,第