甲骨论2012版oracle视频教程-相克军老师_第1页
甲骨论2012版oracle视频教程-相克军老师_第2页
甲骨论2012版oracle视频教程-相克军老师_第3页
甲骨论2012版oracle视频教程-相克军老师_第4页
甲骨论2012版oracle视频教程-相克军老师_第5页
已阅读5页,还剩91页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

分区(partitioning)最早在Oracle8.0中引入,这个过程是将一个表或索引物理地分解为多个更小、更可管理的部分。对索引或表进行分区时可能发生3种情况:使用这些分区表的应用可能运行得更慢;可能运行得更快;有可能没有任何变化。调高可用性可用性的提高源自于每个分区的独立性。对象中一个分区的可用性(或不可用)并不意味着对象本身是不可用的。优化器知道有这种分区机制,会相应地从查询计划中去除未引用的分区。在一个大对象中如果一个分区不可用,你的查询可以消除这个分区而不予考虑,这样Oracle就能成功地处理这个查询。1、如果查询必须涉及这个分区,那么查询报错2、如果查询不涉及这个分区,那么可以正常查询别的分区数据的“摆放”有些随机。通过使用散列分区,我们让Oracle随机地(很可能均匀地)将数据分布到多个分区上。我们无法控制数据要分布到哪个分区上;Oracle会根据生成的散列键值来确定。模拟一个分区故障。减少管理负担例如,假设数据库中有一个10GB的索引。如果需要重建这个索引,而该索引未分区,你就必须将整个10GB的索引作为一个工作单元来重建。尽管可以在线地重建索引,但是要完全重建完整的10GB索引,还是需要占用大量的资源。如果将索引本身划分为10个1GB的分区,各个索引的重建也更快(可能是原来的10倍)。你可能只需要重建全部聚集索引的10%,例如,只是“最新”的数据(活动数据)需要重组,而所有“较旧”的数据(相当静态)不受影响。你发现表中50%的行都是“移植”行,可能想进行修正。建立一个分区表将有利于这个操作。为了“修正”移植行,你往往必须重建对象,在这种情况下,就是要重建一个表。如果有一个100GB的表,就需要在一个非常大的“块”(chunk)上连续地使用ALTERTABLEMOVE来执行这个操作。另一方面,如果你有25个分区,每个分区的大小为4GB,就可以一个接一个地重建各个分区。对于一个未分区对象所能做的工作,分区对象中的单个分区几乎都能做到。你甚至可能发现,移植行都集中在一个很小的分区子集中,因此,可以只重建一两个分区,而不是重建整个表。BIG_TABLE1和BIG_TABLE2都是从BIG_TABLE的一个10,000,000行的实例创建的。BIG_TABLE1是一个常规的未分区表,而BIG_TABLE2是一个散列分区表,有8个分区。createtablebig_table1(ID,OWNER,OBJECT_NAME,SUBOBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID,OBJECT_TYPE,CREATED,LAST_DDL_TIME,TIMESTAMP,STATUS,TEMPORARY,GENERATED,SECONDARY)tablespacebig1asselectID,OWNER,OBJECT_NAME,SUBOBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID,OBJECT_TYPE,CREATED,LAST_DDL_TIME,TIMESTAMP,STATUS,TEMPORARY,GENERATED,SECONDARYfrombig_table.big_table;createtablebig_table2(ID,OWNER,OBJECT_NAME,SUBOBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID,OBJECT_TYPE,CREATED,LAST_DDL_TIME,TIMESTAMP,STATUS,TEMPORARY,GENERATED,SECONDARY)partitionbyhash(id)(partitionpart_1tablespacebig2,partitionpart_2tablespacebig2,partitionpart_3tablespacebig2,partitionpart_4tablespacebig2,partitionpart_5tablespacebig2,partitionpart_6tablespacebig2,partitionpart_7tablespacebig2,partitionpart_8tablespacebig2)asselectID,OWNER,OBJECT_NAME,SUBOBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID,OBJECT_TYPE,CREATED,LAST_DDL_TIME,TIMESTAMP,STATUS,TEMPORARY,GENERATED,SECONDARYfrombig_table.big_table;每个表都在自己的表空间中,所以我们可以很容易地查询数据字典,来查看每个表空间中已分配的空间和空闲空间:selectb.tablespace_name,mbytes_alloc,mbytes_freefrom(selectround(sum(bytes)/1024/1024)mbytes_free,tablespace_namefromdba_free_spacegroupbytablespace_name)a,(selectround(sum(bytes)/1024/1024)mbytes_alloc,tablespace_namefromdba_data_filesgroupbytablespace_name)bwherea.tablespace_name(+)=b.tablespace_nameandb.tablespace_namein('BIG1','BIG2');TABLESPACE MBYTES_ALLOC MBYTES_FREE---------- ------------ -----------BIG1 1496 344BIG2 1496 344ORA10GR1>altertablebig_table1move;altertablebig_table1move*ERRORatline1:ORA-01652:unabletoextendtempsegmentby1024intablespaceBIG1失败了,BIG1表空间中要有足够的空闲空间来放下BIG_TABLE1的完整副本,同时它的原副本仍然保留,简单地说,我们需要一个很短的时间内有大约两倍的存储空间(可能多一点,也可能少一点,这取决于重建后表的大小)。粗略的认为,这个表空间至少需要一倍于这个表的空闲空间。对BIG_TABLE2执行同样的操作:ORA10GR1>altertablebig_table2

move;altertablebig_table2move*ERRORatline1:ORA-14511:cannotperformoperationonapartitionedobject 无法对这个“表”执行MOVE操作;我们必须在表的各个分区上执行这个操作。可以逐个地移动(相应地重建和重组)各个分区:ORA10GR1>altertablebig_table2movepartitionpart_1;Tablealtered.ORA10GR1>altertablebig_table2movepartitionpart_2;Tablealtered.ORA10GR1>altertablebig_table2movepartitionpart_3;Tablealtered.ORA10GR1>altertablebig_table2movepartitionpart_4;Tablealtered.ORA10GR1>altertablebig_table2movepartitionpart_5;Tablealtered.ORA10GR1>altertablebig_table2movepartitionpart_6;Tablealtered.ORA10GR1>altertablebig_table2movepartitionpart_7;Tablealtered.ORA10GR1>altertablebig_table2movepartitionpart_8;Tablealtered.

我们需要的临时资源将显著减少。不仅如此,如果在移动到PART_4后但在PART_5完成“移动”之前系统失败了(例如,掉电),我们并不会丢失以前所做的所有工作,这与执行一个MOVE语句的情况不同。前4个分区仍是“移动”后的状态,等系统恢复时,我们可以从分区PART_5继续处理。ORA10GR1>beginforxin(selectpartition_namefromuser_tab_partitionswheretable_name='BIG_TABLE2')loopexecuteimmediate'altertablebig_table2movepartition'||x.partition_name;endloop;end;使用上面的脚本可以实现。改善语句性能分区最后一个总的(潜在)好处体现在改进语句(SELECT、INSERT、UPDATE、DELETE、MERGE)的性能方面。并行DML修改数据库中数据的语句有可能会执行并行DML(parallelDML,PDML)。采用PDML时,Oracle使用多个线程或进程来执行INSERT、UPDATE或DELETE,而不是执行一个串行进程。在一个有充足I/O带宽的多CPU主机上,对于大规模的DML操作,速度的提升可能相当显著。在Oracle9i以前的版本中,PDML要求必须分区。如果你的表没有分区,在先前的版本中就不能并行地执行这些操作。如果表确实已经分区,Oracle会根据对象所有的物理分区数为对象指定一个最大并行度。从很大程度上讲,在Oracle9i及以后版本中这个限制已经放松,只有一个突出的例外;如果希望在一个表上执行PDML,而且这个表的一个LOB列上有一个位图索引,要并行执行操作就必须对这个表分区;另外并行度就限制为分区数。不过,总的说来,使用PDML并不一定要求进行分区。查询性能在只读查询(SELECT语句)的性能方面,分区对两类特殊操作起作用:1、分区消除(partitionelimination):处理查询时不考虑某些数据分区。我们已经看到了一个分区消除的例子。2、并行操作(paralleloperation):并行全表扫描和并行索引区间扫描就是这种操作的例子。OLTP系统事实上在一个OLTP系统中,查询已经有以下特点:即索引访问相当快,因此,分区不会让索引访问的速度有太大的提高(甚至根本没有任何提高)。这并不是说要绝对避免在OLTP系统中使用分区;而只是说不要指望通过分区来提供大幅的性能提升。尽管有效情况下分区能够改善查询的性能,但是这些情况在大多数OLTP应用中并不成立。不过在OLTP系统中,你还是可以得到另外两个可能的好处:减轻管理负担以及有更高的可用性。有分区的OLTP系统确实也有可能得到效率提示。例如,可以用分区来减少竞争,从而提高并发度。至于并行操作,你可能不希望在一个OLTP系统中执行并行查询。你会慎用并行操作,而是交由DBA来完成重建、创建索引、收集统计信息等工作。数据仓库系统处理许多查询时都要求执行一个全表扫描,但是最后却发现,一方面必须扫描数百万条记录,但另一方面其中大多数记录并不适用于我们的查询。如果使用一种明智的分区机制,就可以实现消除分区,这样在查询某个给定的数据时,就可以只对这个数据进行全面扫描。这在所有可能的解决方案中是最佳的选择。在一个数据仓库/决策支持环境中,会频繁地使用并行查询。因此,诸如并行索引区间扫描或并行快速全面索引扫描等操作对我们很有好处。表分区机制目前Oracle中有4种对表分区的方法:1、区间分区:通过数据范围进行分区。例如,时间戳在Jan-2005内的所有记录都存储在分区1中,时间戳在Feb-2005内的所有记录都存储在分区2中,依此类推。这可能是Oracle中最常用的分区机制。2、散列分区:这是指在一个列(或多个列)上应用一个散列函数,行会按这个散列值放在某个分区中。3、列表分区:指定一个离散值集,来确定应当存储在一起的数据。例如,可以指定STATUS列值在(’A’,’M’,’Z’)中的行放在分区1中,STATUS值在(‘D’,’P’,’Q’)中的行放在分区2中,依此类推。4、组合分区:这是区间分区和散列分区的一种组合,或者是区间分区与列表分区的组合。通过组合分区,你可以先对某些数据应用区间分区,再在区间中根据散列或列表来选择最后的分区。区间分区ORA10GR1>CREATETABLErange_example(range_key_columndate,datavarchar2(20))PARTITIONBYRANGE(range_key_column)(PARTITIONpart_1VALUESLESSTHAN(to_date('01/01/2005','dd/mm/yyyy')),PARTITIONpart_2VALUESLESSTHAN(to_date('01/01/2006','dd/mm/yyyy'));

我们在范围分区的时候,要注意范围分区的值的情况。ORA10GR1>CREATETABLErange_example(range_key_columndate,datavarchar2(20))PARTITIONBYRANGE(range_key_column)(PARTITIONpart_1VALUESLESSTHAN(to_date('01/01/2005','dd/mm/yyyy')),PARTITIONpart_2VALUESLESSTHAN(to_date('01/01/2006','dd/mm/yyyy'))PARTITIONpart_3VALUESLESSTHAN(MAXVALUE);向这个表插入一个行时,这一行肯定会放入三个分区中的某一个分区中,而不会再拒绝任何行,因为分区PART_3可以接受不能放在PART_1或PART_2中的任何RANG_KEY_COLUMN值(即使RANGE_KEY_COLUMN值为null,也会插入到这个新分区中)。散列分区对一个表执行散列分区(hashpartitioning)时,Oracle会对分区键应用一个散列函数,以此确定数据应当放在N个分区中的哪一个分区中。Oracle建议N是2的一个幂(2、4、8、16等),从而得到最佳的总体分布。散列分区设计为能使数据很好地分布在多个不同设备(磁盘)上,为表选择的散列键应当是惟一的一个列或一组列,或者至少有足够多的相异值,以便行能在多个分区上很好地(均匀地)分布。如果你选择一个只有4个相异值的列,并使用两个分区,那么最后可能把所有行都散列到同一个分区上,这就有悖于分区的最初目标!ORA10G>CREATETABLEhash_example(hash_key_columndate,datavarchar2(20))PARTITIONBYHASH(hash_key_column)(partitionpart_1tablespacep1,partitionpart_2tablespacep2);1、如果使用散列分区,你将无从控制一行最终会放在哪个分区中。Oracle会应用散列函数,并根据散列的结果来确定行会放在哪里。2、如果改变散列分区的个数,数据会在所有分区中重新分布(向一个散列分区表增加或删除一个分区时,将导致所有数据都重写,因为现在每一行可能属于一个不同的分区)。3、分区数应该是2的幂如果分区数是2的幂,那么分区将会均匀分布,如果不是2的幂,那么分区将会不均匀分布。我们显然希望得到均匀分布的分区。列表分区列表分区(listpartitioning)是Oracle9iRelease1的一个新特性。它提供了这样一种功能,可以根据离散的值列表来指定一行位于哪个分区。ORA10G>createtablelist_example(state_cdvarchar2(2),datavarchar2(20))partitionbylist(state_cd)(partitionpart_1values('ME','NH','VT','MA'),partitionpart_2values('CT','RI','NY'));1、如果我们想插入列表分区中未指定的一个值,Oracle会向客户应用返回一个合适的错误。ORA10G>insertintolist_examplevalues('VA','data');insertintolist_examplevalues('VA','data')*ERRORatline1:ORA-14400:insertedpartitionkeydoesnotmaptoanypartition ORA10G>altertablelist_exampleaddpartitionpart_3values(DEFAULT);ORA10G>insertintolist_examplevalues('VA','data');1rowcreated. 2、关于DEFAULT的使用,有一点要注意:一旦列表分区表有一个DEFAULT分区,就不能再向这个表中增加更多的分区了。此时必须删除DEFAULT分区,然后增加PART_4,再加回DEFAULT分区。组合分区在组合分区中,顶层分区机制总是区间分区。第二级分区机制可能是列表分区或散列分区(在Oracle9iRelease1及以前的版本中,只支持散列子分区,而没有列表分区)。使用组合分区时,并没有分区段,而只有子分区段。分区本身并没有段(这就类似于分区表没有段)。数据物理的存储在子分区段上,分区成为一个逻辑容器,或者是一个指向实际子分区的容器。ORA10G>CREATETABLEcomposite_example(range_key_columndate,hash_key_columnint,datavarchar2(20))PARTITIONBYRANGE(range_key_column)subpartitionbyhash(hash_key_column)subpartitions2(PARTITIONpart_1VALUESLESSTHAN(to_date('01/01/2005','dd/mm/yyyy'))(subpartitionpart_1_sub_1,subpartitionpart_1_sub_2),PARTITIONpart_2VALUESLESSTHAN(to_date('01/01/2006','dd/mm/yyyy'))(subpartitionpart_2_sub_1,subpartitionpart_2_sub_2);在区间-散列组合分区中,Oracle首先会应用区间分区规则,得出数据属于哪个区间。然后再应用散列函数,来确定数据最后要放在哪个物理分区中。ORA10G>CREATETABLEcomposite_range_list_example(range_key_columndate,code_key_columnint,datavarchar2(20))PARTITIONBYRANGE(range_key_column)subpartitionbylist(code_key_column)(PARTITIONpart_1VALUESLESSTHAN(to_date('01/01/2005','dd/mm/yyyy'))(subpartitionpart_1_sub_1values(1,3,5,7),subpartitionpart_1_sub_2values(2,4,6,8)),PARTITIONpart_2VALUESLESSTHAN(to_date('01/01/2006','dd/mm/yyyy'))(subpartitionpart_2_sub_1values(1,3),subpartitionpart_2_sub_2values(5,7),subpartitionpart_2_sub_3values(2,4,6,8));每个分区的子分区的数目是不一样的。如果用于确定分区的列有修改会发生什么。需要考虑两种情况:1、修改不会导致使用一个不同的分区;行仍属于原来的分区。这在所有情况下都得到支持。2、修改会导致行跨分区移动。只有当表启用了行移动时才支持这种情况;否则,会产生一个错误。ORA10G>insertintorange_example(range_key_column,data)values(to_date('15-dec-200400:00:00','dd-mon-yyyyhh24:mi:ss'),'applicationdata...');ORA10G>insertintorange_example(range_key_column,data)values(to_date('01-jan-200500:00:00','dd-mon-yyyyhh24:mi:ss')-1/24/60/60,'applicationdata...');ORA10G>select*fromrange_examplepartition(part_1);RANGE_KEY DATA--------- --------------------15-DEC-04 applicationdata...31-DEC-04 applicationdata...取其中一行,并更新其RANGE_KEY_COLUMN值,不过更新后它还能放在PART_1中:ORA10G>updaterange_examplesetrange_key_column=trunc(range_key_column)whererange_key_column=to_date('31-dec-200423:59:59','dd-mon-yyyyhh24:mi:ss');成功!再把RANGE_KEY_COLUMN更新为另一个值,但这次更新后的值将导致它属于分区PART_2:ORA10G>updaterange_examplesetrange_key_column=to_date('02-jan-2005','dd-mon-yyyy')whererange_key_column=to_date('31-dec-2004','dd-mon-yyyy');updaterange_example*ERRORatline1:ORA-14402:updatingpartitionkeycolumnwouldcauseapartitionchange 在Oracle8i及以后的版本中,可以在这个表上启用行移动(rowmovement),以允许从一个分区移动到另一个分区。行的ROWID会由于更新而改变:ORA10G>selectrowidfromrange_examplewhererange_key_column=to_date('31-dec-2004','dd-mon-yyyy');ROWID------------------AAARmfAAKAAAI+aAABORA10G>altertablerange_exampleenablerowmovement; ORA10G>updaterange_examplesetrange_key_column=to_date('02-jan-2005','dd-mon-yyyy')whererange_key_column=to_date('31-dec-2004','dd-mon-yyyy');ORA10G>selectrowidfromrange_examplewhererange_key_column=to_date('02-jan-2005','dd-mon-yyyy');ROWID------------------AAARmgAAKAAAI+iAAC在其他一些情况下,ROWID也有可能因为更新而改变。1、更新IOT的主键可能导致ROWID改变,该行的通用ROWID(UROWID)也会改变。2、Oracle10g的FLASHBACKTABLE命令可能改变行的ROWID3、Oracle10g的ALTERTABLESHRINK命令也可能使行的ROWID改变。1、执行行移动时,实际上在内部就好像先删除了这一行,然后再将其重新插入。这会更新这个表上的索引,删除旧的索引条目,再插入一个新条目。2、会完成DELETE再加一个INSERT的相应物理工作。3、尽管在此执行了行的物理删除和插入,在Oracle看来却还是一个更新,因此,不会导致INSERT和DELETE触发器触发,只有UPDATE触发器会触发。4、行移动的开销比正常的UPDATE昂贵得多。如果构建的系统会频繁修改分区键,而且这种修改会导致分区移动,这实在是一个糟糕的设计决策。索引分区索引与表类似,也可以分区。对索引进行分区有两种可能的方法:1、随表对索引完成相应的分区:这也称为局部分区索引(locallypertitionedindex)。每个表分区都有一个索引分区,而且只索引该表分区。一个给定索引分区中的所有条目都指向一个表分区,表分区中的所有行都表示在一个索引分区中。2、按区间对索引分区:这也称为全局分区索引(globallypartitionedindex)。在此,索引按区间分区(或者在Oracle10g中该可以按散列分区),一个索引分区可能指向任何(和所有)表分区。对于全局分区索引,要注意实际上索引分区数可能不同于表分区数。由于全局索引只按区间或散列分区,如果希望有一个列表或组合分区索引,就必须使用局部索引。局部索引会使用底层表相同的机制分区。

注意全局索引的散列分区是Oracle10gRelease1及以后的版本中才有的新特性。在Oracle9i及以前的版本中,只能按区间进行全局分区。分区消除行为如果查询首先访问索引,它是否能消除分区完全取决于查询中的谓词。ORA10G>CREATETABLEpartitioned_table(aint,bint,datachar(20))PARTITIONBYRANGE(a)(PARTITIONpart_1VALUESLESSTHAN(2)tablespacep1,PARTITIONpart_2VALUESLESSTHAN(3)tablespacep2);小于2的值都在分区PART_1中,小于3的值则都在分区PART_2中。创建一个局部前缀索引LOCAL_PREFIXED和一个局部非前缀索引LOCAL_NONPREFIXED。ORA10G>createindexlocal_prefixedonpartitioned_table(a,b)local;Indexcreated.ORA10G>createindexlocal_nonprefixedonpartitioned_table(b)local;Indexcreated.非前缀索引在其定义中没有以A作为其前导列(最前列),这是这一点使之成为一个非前缀索引。ORA10G>insertintopartitioned_tableselectmod(rownum-1,2)+1,rownum,'x'fromall_objects;48967rowscreated.ORA10G>begindbms_stats.gather_table_stats(user,'PARTITIONED_TABLE',cascade=>TRUE);end;/PL/SQLproceduresuccessfullycompleted.向一个分区中插入一些数据,并收集统计信息。将表空间P2离线,其中包含用于表和索引的PART_2分区ORA10G>altertablespacep2offline;Tablespacealtered. 表空间P2离线后,Oracle就无法访问这些特定的索引分区。这就好像是我们遭遇了“介质故障”,导致分区不可用。现在我们查询这个表,来看看不同的查询需要哪些索引分区。第一个查询编写为允许使用局部前缀索引:ORA10G>select*frompartitioned_tablewherea=1andb=1;A B DATA----------

----------

--------------------1

1

x 这个查询成功了,通过查看解释计划,可以看到这个查询为什么能成功。使用LOCAL_PREFIXED的查询成功了。优化器能消除LOCAL_PREFIXED的PART_2不予考虑,因为我们在查询中指定了A=1,而且在计划中可以清楚地看到PSTART和PSTOP都等于1.局部索引的限制:1、要能够实现消除分区,必须将分区键作为where条件ORA10G>select*frompartitioned_tablewhereb=1;ERROR:ORA-00376:file13cannotbereadatthistimeORA-01110:datafile13:'/home/ora10g/.../o1_mf_p2_1dzn8jwp_.dbf'norowsselected ORA10G>deletefromplan_table;4rowsdeleted.ORA10G>explainplanforselect*frompartitioned_tablewhereb=1;Explained.select*fromtable(dbms_xplan.display); select*frompartitioned_tablewhereb=1;没有实现消除分区,因此访问所有分区。是否消除分区,关键看的是是否使用分区键,和前缀索引、非前缀索引没有关系。要使用非前缀索引,必须使用一个允许分区消除的查询。,这样效率才会提高。ORA10G>dropindexlocal_prefixed;Indexdropped.ORA10G>select*frompartitioned_tablewherea=1andb=1;A B DATA---------- ---------- --------------------1 1 x 非前缀索引也能实现消除分区。如果不能实现分区消除,那么oracle将会对多个分区的多个分区索引进行扫描。不必对非前缀索引退避三舍,也不要认为非前缀索引是主要的性能障碍。重点是,要尽可能保证查询包含的谓词允许索引分区消除。使用前缀局部索引可以保证这一点,使用非前缀索引则不能保证。主要是因为前缀索引将分区键作为前导列。如果你频繁地用以下查询来查询先前的表:select...frompartitioned_tablewherea=:aandb=:b; select...frompartitioned_tablewhereb=:b; 可以考虑在(b,a)上使用一个局部非前缀索引。这个索引对于前面的两个查询都是有用的。(a,b)上的局部前缀索引只对第一个查询有用。局部索引和惟一约束为了保证惟一性(这包括UNIQUE约束或PRIMARYKEY约束),如果你想使用一个局部索引来保证这个约束,那么分区键必须包括在约束本身中。这是局部索引的最大限制。Oracle只保证索引分区内部的惟一性,而不能跨分区。这意味着不能一方面在一个TIMESTAMP字段上执行区间分区,而另一方面在ID上有一个主键(使用一个局部分区索引来保证)。Oracle会利用全局索引来保证惟一性。分区键建立分区以后,保证在不同的分区内,分区键一定是不同的。如果建立主键的时候,包括分区键,那么可以保证分区键不同的时候,一定在不同的分区内,分区键相同的时候,一定在同一个分区内。这样局部唯一性约束可以保证整个表的唯一性。创建一个区间分区表,它按一个名为LOAD_TYPE的列分区,却在ID列上有一个主键ORA10G>CREATETABLEpartitioned(load_datedate,idint,constraintpartitioned_pkprimarykey(id))PARTITIONBYRANGE(load_date)(PARTITIONpart_1VALUESLESSTHAN(to_date('01/01/2000','dd/mm/yyyy')),PARTITIONpart_2VALUESLESSTHAN(to_date('01/01/2001','dd/mm/yyyy')));主键索引是一个全局索引,没有进行分区。系统想在id列上建立一个全局索引,发现这个列上已经建立了索引,系统报错。如果这个列上没有建立索引,那么主键就会建立成功。如果这个键上建立了一个全局索引,那么主键也会建立成功,主键将会使用这个索引。全局索引全局索引使用一种有别于底层表的机制进行分区。表可以按一个TIMESTAMP列划分为10个分区,而这个表上的一个全局索引可以按REGION列划分为5个分区。与局部索引不同,全局索引只有一类,这就是前缀全局索引(prefixedglobalindex)。如果全局索引的索引键未从该索引的分区键开始,这是不允许的。这说明,不论用什么属性对索引分区,这些属性都必须是索引键的前几列。CREATETABLEpartitioned(timestampdate,idint)PARTITIONBYRANGE(timestamp)(PARTITIONpart_1VALUESLESSTHAN(to_date('01-jan-2000','dd-mon-yyyy')),PARTITIONpart_2VALUESLESSTHAN(to_date('01-jan-2001','dd-mon-yyyy')));建立了一个分区表,分区键是timestamp。ORA10G>createindexpartitioned_indexonpartitioned(id)GLOBALpartitionbyrange(id)(partitionpart_1valueslessthan(1000),partitionpart_2valueslessthan(MAXVALUE));建立了全局索引以后,对全局索引进行了分区。我们在进行索引搜索的时候,首先根据where

id=100,迅速的确定这个搜索落在哪个索引分区内,实现了索引分区的消减分区。也就是说,我们只需要搜索一个索引树。注意,这个索引中使用了MAXVALUE。MAXVALUE不仅可以用于索引中,还可以用于任何区间分区表中。它表示区间的“无限上界”。在此前的所有例子中,我们都使用了区间的硬性上界(小于<某个值>的值)。不过,全局索引有一个需求,即最高分区(最后一个分区)必须有一个值为MAXVALUE的分区上界。这可以确保底层表中的所有行都能放在这个索引中。ORA10G>altertablepartitionedaddconstraintpartitioned_pkprimarykey(id);因为在id列上有一个全局索引,因此这个主键直接使用这个全局索引。ORA10G>dropindexpartitioned_index;dropindexpartitioned_index*ERRORatline1:ORA-02429:cannotdropindexusedforenforcementofunique/primarykey 这个表示我们建立了的索引被主键使用。ORA10G>createindexpartitioned_index2onpartitioned(timestamp,id)GLOBALpartitionbyrange(id)(partitionpart_1valueslessthan(1000),partitionpart_2valueslessthan(MAXVALUE))partitionbyrange(id)*ERRORatline4:ORA-14038:GLOBALpartitionedindexmustbeprefixed全局分区索引必须能够实现当进行where条件匹配的时候,首先能够消减分区,然后在一个分区内实现索引的搜索。我们可以以timestamp进行分区,也可以以timestamp

id复合进行分区。也就是必须使用索引的前导列进行分区。数据仓库和全局索引原先数据仓库和全局索引是相当互斥的。数据仓库就意味着系统有某些性质,如有大量的数据出入。许多数据仓库都实现了一种滑动窗口(slidingwindow)方法来管理数据,也就是说,删除表中最旧的分区,并为新加载的数据增加一个新分区。在过去(Oracle8i及以前的版本),数据仓库系统都避免使用全局索引,对此有一个很好的原因:全局索引缺乏可用性。大多数分区操作(如删除一个旧分区)都会使全局索引无效,除非重建全局索引,否则无法使用,这会严重地影响可用性,以前往往都是如此。滑动窗口和索引

下面的例子实现了一个经典的数据滑动窗口。在许多实现中,会随着时间的推移向仓库中增加数据,而最旧的数据会老化。在很多时候,这个数据会按一个日期属性进行区间分区,所以最旧的数据多存储在一个分区中,新加载的数据很可能都存储在一个新分区中。每月的加载过程涉及:

1、去除老数据:最旧的分区要么被删除,要么与一个空表交换(将最旧的分区变为一个表),从而允许对旧数据进行归档。

2、加载新数据并建立索引:将新数据加载到一个“工作”表中,建立索引并进行验证。

3、关联新数据:一旦加载并处理了新数据,数据所在的表会与分区表中的一个空分区交换,将表中的这些新加载的数据变成分区表中的一个分区(分区表会变得更大)。在这个例子中,我们将处理每年的数据,并加载2004和2005财政年度的数据。这个表按TIMESTAMP列分区,并创建了两个索引,一个是ID列上的局部分区索引,另一个是TIMESTAMP列上的全局索引:ORA10G>CREATETABLEpartitioned(timestampdate,idint)PARTITIONBYRANGE(timestamp)(PARTITIONfy_2004VALUESLESSTHAN(to_date('01-jan-2005','dd-mon-yyyy')),PARTITIONfy_2005VALUESLESSTHAN(to_date('01-jan-2006','dd-mon-yyyy')));ORA10G>insertintopartitionedpartition(fy_2004)selectto_date('31-dec-2004',’dd-mon-yyyy’)-mod(rownum,360),object_idfromall_objects;ORA10G>insertintopartitionedpartition(fy_2005)selectto_date('31-dec-2005',’dd-mon-yyyy’)-mod(rownum,360),object_idfromall_objects;ORA10G>createindexpartitioned_idx_localonpartitioned(id)LOCAL/ORA10G>createindexpartitioned_idx_globalonpartitioned(timestamp)GLOBAL/数据按财政年度分区,而且最后两年的数据在线。这个表有两个索引:一个是LOCAL索引,另一个是GLOBAL索引。现在正处于年末,我们想做下面的工作:

(1)删除最旧的财政年度数据。我们不想永远地丢掉这个数据,而只是希望它老化,并将其归档。(2)增加最新的财政年度数据。加载、转换、建索引等工作需要一定的时间。我们想做这个工作,但是希望尽可能不影响当前数据的可用性。第一步是为2004财政年度建立一个看上去就像分区表的空表。我们将使用这个表与分区表中的FY_2004分区交换。ORA10G>createtablefy_2004(timestampdate,idint);Tablecreated.ORA10G>createindexfy_2004_idxonfy_2004(id)Indexcreated.对要加载的新数据做同样的工作。我们将创建并加载一个表,其结构就像是现在的分区表。ORA10G>createtablefy_2006(timestampdate,idint);Tablecreated.ORA10G>insertintofy_2006selectto_date('31-dec-2006','dd-mon-yyyy')-mod(rownum,360),object_idfromall_objects;ORA10G>createindexfy_2006_idxonfy_2006(id)nologging; ORA10G>altertablepartitionedexchangepartitionfy_2004withtablefy_2004includingindexeswithoutvalidation/Tablealtered.ORA10G>altertablepartitioneddroppartitionfy_2004/Tablealtered.将分区交换出去,然后删除旧的分区,同时也将分区索引交换出去。ORA10G>altertablepartitionedaddpartitionfy_2006valueslessthan(to_date('01-jan-2007','dd-mon-yyyy'))Tablealtered.ORA10G>altertablepartitionedexchangepartitionfy_2006withtablefy_2006includingindexeswithoutvalidation将表交换进来,同时包括表的索引也交换进来。这个工作会立即完成;这是通过简单的数据字典更新实现的。增加空分区几乎不需要多少时间来处理。然后,将新创建的空分区与满表交换(满表与空分区交换),这个操作也会很快完成。新数据是在线的。到此为止滑动窗口过程几乎不会带来任何停机时间,但是在我们重建全局索引时,需要相当长的时间才能完成。如果查询依赖于这些索引,在此期间它们的运行时查询性能就会受到负面影响,可能根本不会运行,也可能运行时得不到索引提供的好处。所有数据都必须扫描,而且要根据数据重建整个索引。如果表的大小为数百DB,这会占用相当多的资源。从Oracle9i开始,对于分区维护又增加了另一个选项:可以在分区操作期间使用UPDATEGLOBALINEXES子句来维护全局索引。在你删除一个分区、分解一个分区以及在分区上执行任何必要的操作时,Oracle会对全局索引执行必要的修改,保证它是最新的。由于大多数分区操作都会导致全局索引无效,这个特征对于需要提供数据连续访问的系统来说是一个大福音。你会发现,通过牺牲分区操作的速度,可以换取100%的数据可用性(尽管分区操作的总体响应时间会更慢)。简单地说,如果数据仓库不允许有停机时间,而且必须支持数据的滑入滑出等数据仓库技术,这个特性就再合适不过了,但是你必须了解它带来的影响。ORA10G>altertablepartitionedexchangepartitionfy_2004withtablefy_2004includingindexeswithoutvalidationUPDATEGLOBALINDEXESORA10G>altertablepartitioneddroppartitionfy_2004UPDATEGLOBALINDEXESORA10G>altertablepartitionedaddpartitionfy_2006valueslessthan(to_date('01-jan-2007','dd-mon-yyyy'))ORA10G>altertablepartitionedexchangepartitionfy_2006withtablefy_2006includingindexeswithoutvalidationUPDATEGLOBALINDEXES这里要做一个权衡:我们要在全局索引结构上执行INSERT和DELETE操作的相应逻辑操作。删除一个分区时,必须删除可能指向该分区的所有全局索引条目。执行表与分区的交换时,必须删除指向原数据的所有全局索引条目,再插入指向刚滑入的数据的新条目。所以ALTER命令执行的工作量会大幅增加。下面是用runstats来测试整个的性能:execrunStats_pkg.rs_start;altertablepartitionedexchangepartitionfy_2004withtablefy_2004includingindexeswithoutvalidation;altertablepartitioneddroppartitionfy_2004;altertablepartitionedaddpartitionfy_2006valueslessthan(to_date('01-jan-2007','dd-mon-yyyy'));altertablepartitionedexchangepartitionfy_2006withtablefy_2006includingindexeswithoutvalidation;alterindexpartitioned_idx_globalrebuild;execrunStats_pkg.rs_middle;altertablepartitionedexchangepartitionfy_2005withtablefy_2005includingindexeswithoutvalidationupdateglobalindexes;altertablepartitioneddroppartitionfy_2005updateglobalindexes;altertablepartitionedaddpartitionfy_2007valueslessthan(to_date('01-jan-2008','dd-mon-yyyy'));altertablepartitionedexchangepartitionfy_2007withtablefy_2007includingindexeswithoutvalidationupdateglobalindexes;execrunStats_pkg.rs_stop;OLTP和全局索引OLTP系统的特点是会频繁出现许多小的读写事务,一般来讲,在OLTP系统中,首要的是需要快速访问所需的行,而且数据完整性很关键,另外可用性也非常重要。

在OLTP系统中,许多情况下全局索引很有意义。表数据可以按一个键(一个列键)分区。不过,你可能需要以多种不同的方式访问数据。例如,可能会按表中的LOCATION来划分EMPLOYEE数据,但是还需要按以下列快速访问EMPLOYEE数据:1、DEPARTMENT:部门的地理位置很分散。部门和位置之间没有任何关系。2、EMPLOYEE_ID:尽管员工ID能确定位置,但是你不希望必须按EMPLOYEE_ID和LOCATION搜索,因为这样一来索引分区上将不能发生分区消除。而且EMPLOYEE_ID本身必然是惟一的。3、JOB_TITLE:JOB_TITLE和LOCATION之间没有任何关系。任何LOCATION上都可以出现所有JOB_TITLE值。在一个数据仓库中,可以只使用这些键上的局部分区索引,并使用并行索引区间扫描来快速收集大量数据。在这些情况下不必使用索引分区消除。在OLTP系统中则不同,确实需要使用分区消除,并发查询对这些系统不合适;我们要适当地提供索引。因此,需要利用某些字段上的全局索引。我们要满足以下目标:1、快速访问2、数据完整性3、可用性在一个OLTP系统中,可以通过全局索引实现这些目标。我们可能不实现滑动窗口,而且暂时不考虑审计。我们并不分解分区(除非有一个预定的停机时间),也不会移动数据,等等。对于数据仓库中执行的操作,一般来说不会在活动的OLTP系统中执行它们。ORA10G>createtableemp(EMPNONUMBER(4)NOTNULL,ENAMEVARCHAR2(10),JOBVARCHAR2(9),MGRNUMBER(4),HIREDATEDATE,SALNUMBER(7,2),COMMNUMBER(7,2),DEPTNONUMBER(2)NOTNULL,LOCVARCHAR2(13)NOTNULL)partitionbyrange(loc)(partitionp1valueslessthan('C')tablespacep1,partitionp2valueslessthan('D')tablespacep2,partitionp3valueslessthan('N')tablespacep3,partitionp4valueslessthan('Z')tablespacep4);建立一个分区表,分区键是LOC。ORA10G>altertableempaddconstraintemp_pkprimarykey(empno);ORA10G>createindexemp_job_idxonemp(job)GLOBAL ORA10G>createindexemp_dept_idxonemp(deptno)GLOBAL 建立了三个全局索引。insertintoempselecte.*,d.locfromscott.empe,scott.deptdwheree.deptno=d.deptno;插入数据这里的计划显示出对未分区索引EMP_PK(为支持主键所创建)有一个INDEXUNIQUESCAN。然后还有一个TABLEACCESSGLOBALINDEXROWID,其PSTART和PSTOP为ROWID/ROWID,这说明从索引得到ROWID时,它会准确地告诉我们读哪个表分区来得到这一行。这个访问和未分区表效果一样。对于INDEXRANGESCAN,可以看到类似的结果。在此使用了我们的索引,而且可以对底层数据提供高速的OLTP访问。如果索引进行了分区,则必须是前缀索引,并保证索引分区消除;因此,这些索引也是可扩缩的,这说明我们可以对其分区,而且能观察到类似的行为。下面来看可用性方面。在OLTP系统中,全局分区索引与局部分区索引有着同样的高度可用性。通过这个案例:全局索引在访问底层表的时候,也会利用分区表的特性。ORA10G>selectempno,job,locfromempwherejob='CLERK';selectempno,job,locfromempwherejob='CLERK'*ERRORatline1:ORA-00376:file13cannotbereadatthistimeORA-01110:datafile13:'/home/ora10g/oradata/.../o1_mf_p2_1dzn8jwp_.dbf‘所有分区中都有CLERK数据,由于3个表空间离线,这一点确实会对我们带来影响。这是不可避免的,除非我们在JOB上分区,但是这样一来,就会像按LOC分区查询数据一样出现同样的问题。Oracle会“尽其所能”地为你提供数据。ORA10G>selectcount(*)fromempwherejob='CLERK';COUNT(*)----------4 如果可以由索引来回答查询,就要避免TABLEACCESSBYROWID,数据不可用的事实并不重要。分区和性能对总体查询性能来说,分区的影响无非有以下三种可能:1、使你的查询更快2、根本不影响查询的性能3、使你的查询更慢,而且与未分区实现相比,会占用多出几倍的资源在一个数据仓库中,如果查询频繁地全面扫描很大的数据表,通过消除大段的数据,分区能够对这些查询有很好的影响。通过分区消除,90%的数据都可以不考虑。你的查询往往会运

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论