从10046看分区裁剪(下)

 

上篇中我们讨论了单纯数据表段分区的行为特性和方式。本篇我们继续讨论如果有索引对象参与,会是如何呢?

 

3、全局索引Global Index情况

 

Global Index是分区表中使用的一种默认索引方案。对于分区的数据表,Global Index形成的是一个覆盖所有分区的索引树结构,对应的是一个段对象。

行业里面对于Global Index的“声誉”并不是很好。一般优化人员对于分区表索引,都是推荐使用Local Index,而非Global Index。这样的决策主要是针对两个方面的考量:管理方面和性能优化方面。管理方面主要是Global Index失效问题。Global Index覆盖到整个数据表所有分区,分区表进行归档销毁动作的时候,通常会选择分区摘除动作。Drop partition动作虽然很快,效果也很好,但是会直接导致global index的失效。Index的失效会直接引起系统性能下降。

在性能优化方面,Global Index和分区表是“相斥”的。SQL语句走分区裁剪,就不会选择Global Index。反之选择了Global Index,直接定位结果集合rowid,也就没有必要进行数据表分区裁剪。作为两个都需要消耗优化资源的方案,无论哪个路径,都需要“闲置”一种优化策略。这种的确是不能让优化人员接受的。

下面我们进行一次10046的执行计划。

 

SQL> create index idx_t_part_id on t_part(object_id);

Index created

 

SQL> exec dbms_stats.gather_table_stats(user,'T_PART',cascade => true);

PL/SQL procedure successfully completed

 

查看执行计划:

 

SQL> explain plan for select * from t_part where owner='SYS' and object_id=1000;

 

Explained

 

SQL> select * from table(dbms_xplan.display)

  2  ;

 

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

Plan hash value: 2004327352

--------------------------------------------------------------------------------

| Id  | Operation                          | Name          | Rows  | Bytes | Cos

--------------------------------------------------------------------------------

|   0 | SELECT STATEMENT                   |               |     1 |    97 |

|*  1 |  TABLE ACCESS BY GLOBAL INDEX ROWID| T_PART        |     1 |    97 |

|*  2 |   INDEX RANGE SCAN                 | IDX_T_PART_ID |     1 |       |

--------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

   1 - filter("OWNER"='SYS')

   2 - access("OBJECT_ID"=1000)

 

15 rows selected

 

Oracle承认T_PART是一个分区表,索引IDX_T_PART是上面的Global Index。这个过程中没有进行分区裁剪,也就是直接利用Global Index路径进行检索。

下面我们通过10046来进行检查。

 

 

SQL> select value from v$diag_info where name='Default Trace File';

 

VALUE

-----------------------------------------

/u01/app/diag/rdbms/ora11g/ora11g/trace/ora11g_ora_2035.trc

 

SQL> alter system flush shared_pool;

System altered.

 

SQL> alter system flush buffer_cache;

System altered.

 

Session level进行10046监控。

 

 

SQL> alter session set events='10046 trace name context forever, level 12';

Session altered.

 

SQL>  select * from t_part where owner='SYS' and object_id=1000;

(结果略)

 

SQL> alter session set events='10046 trace name context off';

Session altered.

 

Trace文件中片段如下:

 

=====================

PARSING IN CURSOR #3075330936 len=58 dep=0 uid=0 oct=3 lid=0 tim=1403366711465450 hv=2083714074 ad='35d34494' sqlid='2kcw7yjy35x0u'

 select * from t_part where owner='SYS' and object_id=1000

END OF STMT

PARSE #3075330936:c=243962,e=363698,p=33,cr=451,cu=0,mis=1,r=0,dep=0,og=1,plh=2004327352,tim=1403366711465446

EXEC #3075330936:c=0,e=92,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=2004327352,tim=1403366711465912

WAIT #3075330936: nam='SQL*Net message to client' ela= 10 driver id=1650815232 #bytes=1 p3=0 obj#=-1 tim=1403366711466062

WAIT #3075330936: nam='db file sequential read' ela= 182 file#=1 block#=93129 blocks=1 obj#=87169 tim=1403366711466584

WAIT #3075330936: nam='db file sequential read' ela= 136 file#=1 block#=93132 blocks=1 obj#=87169 tim=1403366711466948

WAIT #3075330936: nam='db file sequential read' ela= 105 file#=1 block#=93837 blocks=1 obj#=87165 tim=1403366711467256

FETCH #3075330936:c=1000,e=1186,p=3,cr=3,cu=0,mis=0,r=1,dep=0,og=1,plh=2004327352,tim=1403366711467497

WAIT #3075330936: nam='SQL*Net message from client' ela= 716 driver id=1650815232 #bytes=1 p3=0 obj#=87165 tim=1403366711468310

FETCH #3075330936:c=0,e=18,p=0,cr=1,cu=0,mis=0,r=0,dep=0,og=1,plh=2004327352,tim=1403366711468571

STAT #3075330936 id=1 cnt=1 pid=0 pos=1 obj=87164 op='TABLE ACCESS BY GLOBAL INDEX ROWID T_PART PARTITION: 1 1 (cr=4 pr=3 pw=0 time=1235 us cost=2 size=97 card=1)'

STAT #3075330936 id=2 cnt=1 pid=1 pos=1 obj=87169 op='INDEX RANGE SCAN IDX_T_PART_ID (cr=3 pr=2 pw=0 time=771 us cost=1 size=0 card=1)'

WAIT #3075330936: nam='SQL*Net message to client' ela= 4 driver id=1650815232 #bytes=1 p3=0 obj#=87165 tim=1403366711469180

 

*** 2014-06-22 00:05:17.550

WAIT #3075330936: nam='SQL*Net message from client' ela= 6080882 driver id=1650815232 #bytes=1 p3=0 obj#=87165 tim=1403366717550106

CLOSE #3075330936:c=0,e=46,dep=0,type=0,tim=1403366717551418

=====================

 

传统的索引路径都是从索引段入手,访问到索引根节点块,之后逐层向下访问进行定位。这个过程进行的大都是单块读动作。从Trace File结果,Oracle首先进行了对file#=1 block#=93129 blocks=1,对象编号为87169。

 

SQL> col object_name for a30;

SQL> select object_name, subobject_name, object_type from dba_objects where object_id=87169;

 

OBJECT_NAME                    SUBOBJECT_ OBJECT_TYPE

------------------------------ ---------- -------------------

IDX_T_PART_ID                             INDEX

 

对对象87169,Oracle进行了两次单块访问。一次是段头块访问,另一次是定位到叶子节点。获取到符合条件的叶子节点rowid之后,就直接回表到数据表指定的块中。这也就是之后的第三次单块访问,编号为87165。

 

SQL> select object_name, subobject_name, object_type from dba_objects where object_id=87165;

 

OBJECT_NAME                    SUBOBJECT_ OBJECT_TYPE

------------------------------ ---------- -------------------

T_PART                         T_PART_P0  TABLE PARTITION

 

在这个过程中,我们没有看到分区裁剪,只看到了普通索引定位检索过程。我们说:对于全局索引和分区表而言,SQL执行计划是相斥的。这个实验中,我们看到了分区裁剪被闲置的情况,在另外的一些情况下,是可能出现索引被闲置的情况的。

 

4、局部Local Index

 

最后我们来看看Local Index的情况。和Global Index不同,Local Index是由两个特点值得关注。一个是索引段分区,从传统的一个单树结构,演变到多树多索引段的结构。另一个是分区策略,local索引的分区策略(分区种类和分区键)和数据表保持一致。

对于Local Index,要记住:除了索引中携带的索引字段信息外,还包括了分区键和分区条件。Local Index是能够利用分区裁剪和索引双重特性的解决方案。

下面我们创建本地索引。

 

 

SQL> drop index idx_t_part_id;

Index dropped

 

SQL> create index idx_t_part_id_local on t_part(object_id) local;

Index created

 

此时,索引段按照数据表段方式进行分区,分区名称和数据表分区相同。

 

SQL> select PARTITION_NAME, SEGMENT_TYPE, HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS from dba_segments where owner='SYS' and segment_name='IDX_T_PART_ID_LOCAL';

 

PARTITION_ SEGMENT_TYPE       HEADER_FILE HEADER_BLOCK      BYTES     BLOCKS    EXTENTS

---------- ------------------ ----------- ------------ ---------- ---------- ----------

T_PART_P0  INDEX PARTITION              1        93128     720896         88         11

T_PART_P1  INDEX PARTITION              1        97952      65536          8          1

T_PART_P2  INDEX PARTITION              1        97960      65536          8          1

T_PART_P3  INDEX PARTITION              1        97968     917504        112         14

 

执行计划分析:

 

SQL> explain plan for select * from t_part where owner='SYS' and object_id=1000;

 

Explained

 

SQL> select * from table(dbms_xplan.display)

  2  ;

 

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

Plan hash value: 2413422785

--------------------------------------------------------------------------------

| Id  | Operation                          | Name                | Rows  | Bytes

--------------------------------------------------------------------------------

|   0 | SELECT STATEMENT                   |                     |     1 |    97

|   1 |  PARTITION LIST SINGLE             |                     |     1 |    97

|   2 |   TABLE ACCESS BY LOCAL INDEX ROWID| T_PART              |     1 |    97

|*  3 |    INDEX RANGE SCAN                | IDX_T_PART_ID_LOCAL |     1 |

--------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

   3 - access("OBJECT_ID"=1000)

 

15 rows selected

 

我们在执行计划中看到了分区裁剪动作,并且也看到了索引路径。这里一个问题:数据表和索引都分区了,这个裁剪裁剪的是谁?我们通过10046来进行验证。

 

 

SQL> select value from v$diag_info where name='Default Trace File';

VALUE

--------------------------------------

/u01/app/diag/rdbms/ora11g/ora11g/trace/ora11g_ora_2165.trc

 

SQL> alter system flush shared_pool;

System altered.

 

SQL> alter system flush buffer_cache;

System altered.

 

开启10046检查。

 

 

SQL> alter session set events='10046 trace name context forever, level 12';

Session altered.

 

SQL> select * from t_part where owner='SYS' and object_id=1000;

 

 

SQL> alter session set events='10046 trace name context off';

Session altered.

 

对应的Trace File信息片段如下:

 

 

=====================

PARSING IN CURSOR #3074950008 len=57 dep=0 uid=0 oct=3 lid=0 tim=1403367710144497 hv=919498438 ad='2d9290f8' sqlid='2q5x87wvcwvq6'

select * from t_part where owner='SYS' and object_id=1000

END OF STMT

PARSE #3074950008:c=242963,e=404745,p=37,cr=462,cu=0,mis=1,r=0,dep=0,og=1,plh=2413422785,tim=1403367710144492

EXEC #3074950008:c=0,e=146,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=2413422785,tim=1403367710144857

WAIT #3074950008: nam='SQL*Net message to client' ela= 7 driver id=1650815232 #bytes=1 p3=0 obj#=-1 tim=1403367710144940

WAIT #3074950008: nam='db file sequential read' ela= 152 file#=1 block#=93129 blocks=1 obj#=87172 tim=1403367710146094

WAIT #3074950008: nam='db file sequential read' ela= 149 file#=1 block#=93132 blocks=1 obj#=87172 tim=1403367710146520

WAIT #3074950008: nam='db file sequential read' ela= 373 file#=1 block#=93837 blocks=1 obj#=87165 tim=1403367710147080

FETCH #3074950008:c=999,e=1367,p=3,cr=3,cu=0,mis=0,r=1,dep=0,og=1,plh=2413422785,tim=1403367710147177

WAIT #3074950008: nam='SQL*Net message from client' ela= 760 driver id=1650815232 #bytes=1 p3=0 obj#=87165 tim=1403367710148032

FETCH #3074950008:c=0,e=22,p=0,cr=1,cu=0,mis=0,r=0,dep=0,og=1,plh=2413422785,tim=1403367710148339

STAT #3074950008 id=1 cnt=1 pid=0 pos=1 obj=0 op='PARTITION LIST SINGLE PARTITION: KEY KEY (cr=4 pr=3 pw=0 time=1453 us cost=2 size=97 card=1)'

STAT #3074950008 id=2 cnt=1 pid=1 pos=1 obj=87164 op='TABLE ACCESS BY LOCAL INDEX ROWID T_PART PARTITION: 1 1 (cr=4 pr=3 pw=0 time=1422 us cost=2 size=97 card=1)'

STAT #3074950008 id=3 cnt=1 pid=2 pos=1 obj=87171 op='INDEX RANGE SCAN IDX_T_PART_ID_LOCAL PARTITION: 1 1 (cr=3 pr=2 pw=0 time=804 us cost=1 size=0 card=1)'

WAIT #3074950008: nam='SQL*Net message to client' ela= 4 driver id=1650815232 #bytes=1 p3=0 obj#=87165 tim=1403367710148876

 

*** 2014-06-22 00:22:03.505

WAIT #3074950008: nam='SQL*Net message from client' ela= 13356053 driver id=1650815232 #bytes=1 p3=0 obj#=87165 tim=1403367723504972

CLOSE #3074950008:c=0,e=142,dep=0,type=0,tim=1403367723506903

=====================

 

Oracle直接定位到了object_id=87172,我们来查看一下这个对象是谁?

 

 

SQL> select object_name, subobject_name, object_type from dba_objects where object_id=87172;

 

OBJECT_NAME                    SUBOBJECT_ OBJECT_TYPE

------------------------------ ---------- -------------------

IDX_T_PART_ID_LOCAL            T_PART_P0  INDEX PARTITION

 

是索引段的分区!说明Oracle进行本地索引分区的时候,是进行的索引段的分区裁剪。同标准分区裁剪相同,Oracle首先利用recursive call获取到分区、分区索引的基本元数据信息,其中最重要的是分区键和分区信息。这就提供了Oracle直接就访问到分区索引树的条件,数据段分区也就不需要了。

 

5、结论

 

分区表、Global Index和Local Index,是会影响到管理运维、性能优化的重要组合概念。在SQL层面,尽可能的利用已有的优化策略获取到最优的性能,是我们决策问题的出发点。所有的优化策略,都需要付出管理、性能的成本付出,以最少的付出,获取最大的综合性能,也就是我们优化人员的职分所在。


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/17203031/viewspace-1191341/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/17203031/viewspace-1191341/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值