主题:我们将简要地介绍以下的Oracle主题:
--外部调整:我们应该记住Oracle并不是单独运行的。因此我们将查看一下通过调整外部调整Oracle服务器以得到高的性能。--Rowre-sequencing以减少磁盘I/O:我们应该懂得Oracle调优最重要的目标是减少I/O。--OracleSQL调整调整。OracleSQL调整是Oracle调整中最重要的领域之一,只要通过一些简单的SQL调优规则就可以大幅度地提升SQL语句的性能,这是一点都不奇怪的。--调整Oracle排序排序:排序对于Oracle性能也是有很大影响的。--调整Oracle的竞争的竞争:表和索引的参数设置对于UPDATE和INSERT的性能有很大的影响。我们首先从调整Oracle外部的环境开始。如果内存和CPU的资源不足的话,任何的Oracle调整都是没有帮助的。
外部的性能问题
Oracle并不是单独运行的。Oracle数据库的性能和外部的环境有很大的关系。这些外部的条件包括有:.CPU--CPU资源的不足令查询变慢。当查询超过了Oracle服务器的CPU性能时,你的数据库性能就受到CPU的限制。.内存--可用于Oralce的内存数量也会影响SQL的性能,特别是在数据缓冲和内存排序方面。.网络--大量的Net8通信令SQL的性能变慢。许多新手都错误的认为应该首先调整Oracle数据库,而不是先确认外部资源是否足够。实际上,如果外部环境出现瓶颈,再多的Oracle调整都是没有帮助的。在检查Oracle的外部环境时,有两个方面是需要注意的:1、当运行队列的数目超过服务器的CPU数量时,服务器的性能就会受到CPU的限制。补救的方法是为服务器增加额外的CPU或者关闭需要很多处理资源的组件,例如OracleParallelQuery。2、内存分页。当内存分页时,内存容量已经不足,而内存页是与磁盘上的交换区进行交互的。补救的方法是增加更多的内存,减少OracleSGA的大小,或者关闭Oracle的多线程服务器。可以使用各种标准的服务器工具来得到服务器的统计数据,例如vmstat,glance,top和sar。DBA的目标是确保数据库服务器拥有足够的CPU和内存资源来处理Oracle的请求。以下让我们来看一下Oracle的row-resequencing是如何能够极大地减少磁盘I/O的。
Row-resequencing(行的重新排序)行的重新排序)
就象我们上面提到的,有经验的OracleDBA都知道I/O是响应时间的最大组成部分。其中磁盘I/O特别厉害,因为当Oracle由磁盘上的一个数据文件得到一个数据块时,读的进程就必须等待物理I/O操作完成。磁盘操作要比数据缓冲慢10,000倍。因此,如果可以令I/O最小化,或者减少由于磁盘上的文件竞争而带来的瓶颈,就可以大大地改善
Oracle数据库的性能。如果系统响应很慢,通过减少磁盘I/O就可以有一个很快的改善。如果在一个事务中通过按一定的范围搜索primary-key索引来访问表,那么重新以CTAS的方法组织表将是你减少I/O的首要策略。通过在物理上将行排序为和primary-key索引一样的顺序,就可以加快获得数据的速度。就象磁盘的负载平衡一样,行的重新排序也是很简单的,而且也很快。通过与其它的DBA管理技巧一起使用,就可以在高I/O的系统中大大地减少响应的时间。在高容量的在线事务处理环境中(onlinetransactionprocessing,OLTP),数据是由一个primary索引得到的,重新排序表格的行就可以令连续块的顺序和它们的primary索引一样,这样就可以在索引驱动的表格查询中,减少物理I/O并且改善响应时间。这个技巧仅在应用选择多行的时候有用,或者在使用索引范围搜索和应用发出多个查询来得到连续的key时有效。对于随机的唯一primary-key(主键)的访问将不会由行重新排序中得到好处。让我们看一下它是如何工作的。考虑以下的一个SQL的查询,它使用一个索引来得到100行:selectsalaryfromemployeewherelast_namelike'B%';这个查询将会使用last_name_index,搜索其中的每一行来得到目标行。这个查询将会至少使用100次物理磁盘的读取,因为employee的行存放在不同的数据块中。不过,如果表中的行已经重新排序为和last_name_index的一样,同样的查询又会怎样处理呢?我们可以看到这个查询只需要三次的磁盘I/O就读完全部100个员工的资料(一次用作索引的读取,两次用作数据块的读取),减少了97次的块读取。重新排序带来的性能改善的程度在于在你开始的时候行的乱序性如何,以及你需要由序列中访问多少行。至于一个表中的行与索引的排序键的匹配程度,可以查看数据字典中的dba_indexes和dba_tables视图得到。在dba_indexes的视图中,查看clustering_factor列。如果clustering_factor的值和表中的块数目大致一样,那么你的表和索引的顺序是一样的。不过,如果clustering_factor的值接近表中的行数目,那就表明表格中的行和索引的顺序是不一样的。行重新排序的作用是不可以小看的。在需要进行大范围的索引搜索的大表中,行重新排序可以令查询的性能提高三倍。一旦你已经决定重新排序表中的行,你可以使用以下的工具之一来重新组织表格。.使用Oracle的CreateTableAsSelect(CTAS)语法来拷贝表格.Oracle9i自带的表格重新组织工具以下,我们来看以下
SQL语句的调优。语句的调优。
SQL调优Oracle的SQL调优是一个复杂的主题,甚至是需要整本书来介绍OracleSQL调优的细微差别。不过有一些基本的规则是每个OracleDBA都需要跟从的,这些规则可以改善他们系统的性能。SQL调优的目标是简单的:.消除不必要的大表全表搜索:不必要的全表搜索导致大量不必要的I/O,从而拖慢整个数据库的性能。调优专家首先会根据查询返回的行数目来评价SQL。在一个有序的表中,如果查询返回少于40%的行,或者在一个无序的表中,返回少于7%的行,那么这个查询都可以调整为使用一个索引来代替全表搜索。对于不必要的全表搜索来说,最常见的调优方法是增加索引。可以在表中加入标准的B树索引,也可以加入bitmap和基于函数的索引。要决定是否消除一个全表搜索,你可以仔细检查索引搜索的I/O开销和全表搜索的开销,它们的开销和数据块的读取和可能的并行执行有关,并将两者作对比。在一些情况
下,一些不必要的全表搜索的消除可以通过强制使用一个index来达到,只需要在SQL语句中加入一个索引的提示就可以了。.在全表搜索是一个最快的访问方法时,将小表的全表搜索放到缓存中,调优专家应该确保有一个专门的数据缓冲用作行缓冲。在Oracle7中,你可以使用altertablexxxcache语句,在Oracle8或以上,小表可以被强制为放到KEEP池中缓冲。.确保最优的索引使用:对于改善查询的速度,这是特别重要的。有时Oracle可以选择多个索引来进行查询,调优专家必须检查每个索引并且确保Oracle使用正确的索引。它还包括bitmap和基于函数的索引的使用。.确保最优的JOIN操作:有些查询使用NESTEDLOOPjoin快一些,有些则是HASHjoin快一些,另外一些则是sort-mergejoin更快。这些规则看来简单,不过它们占SQL调优任务的90%,并且它们也无需完全懂得OracleSQL的内部运作。以下我们来简单概览以下OracleSQL的优化。减少排序我们首先简要查看Oracle的排序,并且看一看排序操作是如何影响性能的。
调整Oracle的排序操作
排序是SQL语法中一个小的方面,但很重要,在Oracle的调整中,它常常被忽略。当使用createindex、ORDERBY或者GROUPBY的语句时,Oracle数据库将会自动执行排序的操作。通常,在以下的情况下Oracle会进行排序的操作:使用Orderby的SQL语句使用Groupby的SQL语句在创建索引的时候distinctminusunion进行tablejoin时,由于现有索引的不足而导致SQL优化器调用MERGESORT当与Oracle建立起一个session时,在内存中就会为该session分配一个私有的排序区域。如果该连接是一个专用的连接(dedicatedconnection),那么就会根据init.ora中sort_area_size参数的大小在内存中分配一个ProgramGlobalArea(PGA)。如果连接是通过多线程服务器建立的,那么排序的空间就在large_pool中分配。不幸的是,对于所有的session,用做排序的内存量都必须是一样的,我们不能为需要更大排序的操作分配额外的排序区域。因此,设计者必须作出一个平衡,在分配足够的排序区域以避免发生大的排序任务时出现磁盘排序(disksorts)的同时,对于那些并不需要进行很大排序的任务,就会出现一些浪费。当然,当排序的空间需求超出了sort_area_size的大小时,这时将会在TEMP表空间中分页进行磁盘排序。磁盘排序要比内存排序大概慢14,000倍。上面我们已经提到,私有排序区域的大小是有init.ora中的sort_area_size参数决定的。每个排序所占用的大小由init.ora中的sort_area_retained_size参数决定。当排序不能在分配的空间中完成时,就会使用磁盘排序的方式,即在Oracle实例中的临时表空间中进行。磁盘排序的开销是很大的,有几个方面的原因。首先,和内存排序相比较,它们特别慢;而且磁盘排序会消耗临时表空间中的资源。Oracle还必须分配缓冲池块来保持临时表空间中的块。无论什么时候,内存排序都比磁盘排序好,磁盘排序将会令任务变慢,并且会影响Oracle实例的当前任务的执行。还有,过多的磁盘排序将会令freebufferwaits的值变高,从而令其它任务的数据块由缓冲中移走。接着,让我们看一下Oracle的竞争,并且看一下表的存储参数的设置是如何影响SQLUPDATE和INSERT语句的性能的。
调整Oracle的竞争
Oracle的其中一个优点时它可以管理每个表空间中的自由空间。Oracle负责处理表和索引的空间管理,这样就可以让我们无需懂得Oracle的表和索引的内部运作。不过,对于有经验的Oracle调优专家来说,他需要懂得Oracle是如何管理表的extent和空闲的数据块。对于调整拥有高的insert或者update的系统来说,这是非常重要的。要精通对象的调整,你需要懂得freelists和freelist组的行为,它们和pctfree及pctused参数的值有关。这些知识对于企业资源计划(ERP)的应用是特别重要的,因为在这些应用中,不正确的表设置通常是DML语句执行慢的原因。对于初学者来说,最常见的错误是认为默认的Oracle参数对于所有的对象都是最佳的。除非磁盘的消耗不是一个问题,否则在设置表的pctfree和pctused参数时,就必须考虑平均的行长和数据库的块大小,这样空的块才会被有效地放到freelists中。当这些设置不正确时,那些得到的freelists也是"dead"块,因为它们没有足够的空间来存储一行,这样将会导致明显的处理延迟。Freelists对于有效地重新使用Oracle表空间中的空间是很重要的,它和pctfree及pctused这两个存储参数的设置直接相关。通过将pctused设置为一个高的值,这时数据库就会尽快地重新使用块。不过,高性能和有效地重新使用表的块是对立的。在调整Oracle的表格和索引时,需要认真考虑究竟需要高性能还是有效的空间重用,并且据此来设置表的参数。以下我们来看一下这些freelists是如何影响Oracle的性能的。当有一个请求需要插入一行到表格中时,Oracle就会到freelist中寻找一个有足够的空间来容纳一行的块。你也许知道,freelist串是放在表格或者索引的第一个块中,这个块也被称为段头(segmentheader)。pctfree和pctused参数的唯一目的就是为了控制块如何在freelists中进出。虽然freelistlink和unlink是简单的Oracle功能,不过设置freelistlink(pctused)和unlink(pctfree)对Oracle的性能确实有影响。由DBA的基本知识知道,pctfree参数是控制freelistun-links的(即将块由freelists中移除)。设置pctfree=10意味着每个块都保留10%的空间用作行扩展。pctused参数是控制freelistre-links的。设置pctused=40意味着只有在块的使用低于40%时才会回到表格的freelists中。许多新手对于一个块重新回到freelists后的处理都有些误解。其实,一旦由于一个删除的操作而令块被重新加入到freelist中,它将会一直保留在freelist中即使空间的使用超过了60%,只有在到达pctfree时才会将数据块由freelist中移走。表格和索引存储参数设置的要求总结以下的一些规则是用来设置freelists,freelistgroups,pctfree和pctused存储参数的。你也知道,pctused和pctfree的值是可以很容易地通过altertable命令修改的,一个好的DBA应该知道如何设置这些参数的最佳值。有效地使用空间和高性能之间是有矛盾的,而表格的存储参数就是控制这个方面的矛盾:.对于需要有效地重新使用空间,可以设置一个高的pctused值,不过副作用是需要额外的I/O。一个高的pctused值意味着相对满的块都会放到freelist中。因此,这些块在再次满之前只可以接受几行记录,从而导致更多的I/O。.追求高性能的话,可以将pctused设置为一个低的值,这意味着Oracle不会将数据块放到freelists中直到它几乎是空的。那么块将可以在满之前接收更多的行,因此可以减少插入操作的I/O。要记住Oracle扩展新块的性能要比重新使用现有的块高。对于Oracle来说,扩展一个表比管理freelists消耗更少的资源。让我们来回顾一下设置对象存储参数的一些常见规则:.经常将pctused设置为可以接收一条新行。对于不能接受一行的freeblocks对于我
们来说是没有用的。如果这样做,将会令Oracle的性能变慢,因为Oracle将在扩展表来得到一个空的块之前,企图读取5个"dead"的freeblock。.表格中chainedrows的出现意味着pctfree太低或者是db_block_size太少。在很多情况下,RAW和LONGRAW列都很巨大,以至超过了Oracle的最大块的大小,这时chainedrows是不可以避免的。.如果一个表有同时插入的SQL语句,那么它需要有同时删除的语句。运行单一个一个清除的工作将会把全部的空闲块放到一个freelist中,而没有其它包含有任何空闲块的freelists出现。.freelist参数应该设置为表格同时更新的最大值。例如,如果在任何时候,某个表最多有20个用户执行插入的操作,那么该表的参数应该设置为freelists=20。应记住的是freelistgroups参数的值只是对于OracleParallelServer和RealApplicationClusters才是有用的。对于这类Oracle,freelistgroups应该设置为访问该表格的OracleParallelServer实例的数目。
索引详解
什么是索引索引是建立在表的一列或多个列上的辅助对象,目的是加快访问表中的数据;Oracle存储索引的数据结构是B*树,位图索引也是如此,只不过是叶子节点不同B*数索引;索引由根节点、分支节点和叶子节点组成,上级索引块包含下级索引块的索引数据,叶节点包含索引数据和确定行实际位置的rowid。使用索引的目的加快查询速度减少I/O操作消除磁盘排序何时使用索引查询返回的记录数排序表<40%非排序表<7%表的碎片较多(频繁增加、删除)索引的种类非唯一索引(最常用)唯一索引位图索引局部有前缀分区索引局部无前缀分区索引全局有前缀分区索引散列分区索引基于函数的索引
管理索引的准则在表中插入数据后创建索引。在用SQL*Loader或import工具插入或装载数据后,建立索引比较有效;索引正确的表和列。经常检索排序大表中40%或非排序表7%的行,建议建索引;。为了改善多表关联,索引列用于联结;。列中的值相对比较唯一;。取值范围(大:B*树索引,小:位图索引);。Date型列一般适合基于函数的索引;。列中有许多空值,不适合建立索引为性能而安排索引列。经常一起使用多个字段检索记录,组合索引比单索引更有效;。把最常用的列放在最前面,例:dx_groupid_serv_id(groupid,serv_id),在where条件中使用groupid或groupid,serv_id,查询将使用索引,若仅用到serv_id字段,则索引无效;。合并/拆分不必要的索引。索引对性能的影响索引对性能的影响。一个表可以有几百个索引(你会这样做吗?),但是对于频繁插入和更新表,索引越多系统CPU,I/O负担就越重;。建议每张表不超过5个索引。删除不再需要的索引。索引无效,集中表现在该使用基于函数的索引或位图索引,而使用了B*树索引;。应用中的查询不使用索引;。重建索引之前必须先删除索引,若用alterindex…rebuild重建索引,则不必删除索引。索引数据块空间使用。创建索引时指定表空间,特别是在建立主键时,应明确指定表空间;。合理设定pctfress,注意:不能给索引指定pctused;。估计索引的大小和合理地设置存储参数,默认为表空间大小,或initial与next设置成一样大。考虑并行创建索引。对大表可以采用并行创建索引,在并行创建索引时,存储参数被每个查询服务器进程分别使用,例如:initial为1M,并行度为8,则创建索引期间至少要消耗8M空间;考虑用nologging创建索引
。对大表创建索引可以使用nologging来减少重做日志;。节省重做日志文件的空间;。缩短创建索引的时间;。改善了并行创建大索引时的性能。怎样建立最佳索引明确地创建索引createindexindex_nameontable_name(field_name)tablespacetablespace_namepctfree5initrans2maxtrans255storage(minextents1maxextents16382pctincrease0);创建基于函数的索引。常用与UPPER、LOWER、TO_CHAR(date)等函数分类上,例:createindexidx_funconemp(UPPER(ename))tablespacetablespace_name;创建位图索引。对基数较小,且基数相对稳定的列建立索引时,首先应该考虑位图索引,例:createbitmapindexidx_bitmonclass(classno)tablespacetablespace_name;明确地创建唯一索引。可以用createuniqueindex语句来创建唯一索引,例:createuniqueindexdept_unique_idxondept(dept_no)tablespaceidx_1;创建与约束相关的索引。可以用usingindex字句,为与unique和primarykey约束相关的索引,例如:altertabletable_nameaddconstraintPK_primary_keynameprimarykey(field_name)usingindextablespacetablespace_name;如何创建局部分区索引。基础表必须是分区表;。分区数量与基础表相同;。每个索引分区的子分区数量与相应的基础表分区相同;。基础表的子分区中的行的索引项,被存储在该索引的相应的子分区中,例如:CreateIndexTG_CDR04_SERV_ID_IDXOnTG_CDR04(SERV_ID)
Pctfree5TablespaceTBS_AK01_IDXStorage(MaxExtents32768PctIncrease0FreeLists1FreeListGroups1)local/如何创建范围分区的全局索引。基础表可以是全局表和分区表。createindexidx_start_dateontg_cdr01(start_date)globalpartitionbyrange(start_date)(partitionp01_idxvlaueslessthan(‘0106’)partitionp01_idxvlaueslessthan(‘0111’)…partitionp01_idxvlaueslessthan(‘0401’))/重建现存的索引重建现存的索引的当前时刻不会影响查询;重建索引可以删除额外的数据块;提高索引查询效率;alterindexidx_namerebuildnologging;对于分区索引:alterindexidx_namerebuildpartitionpartiton_namenologging;要删除索引的原因。不再需要的索引;。索引没有针对其相关的表所发布的查询提供所期望的性能改善;。应用没有用该索引来查询数据;。该索引无效,必须在重建之前删除该索引;。该索引已经变的太碎了,必须在重建之前删除该索引;。语句:dropindexidx_name;dropindexidx_namedroppartitionpartition_name;建立索引的代价基础表维护时,系统要同时维护索引,不合理的索引将严重影响系统资源,主要表现在CPU和I/O上;插入、更新、删除数据产生大量dbfilesequentialread锁等待;
SQL优化器简介基于规则的优化器。总是使用索引。总是从驱动表开始(from子句最右边的表)。只有在不可避免的情况下,才使用全表扫描。任何索引都可以基于成本的优化器。需要表、索引的统计资料Analyzetablecustomercomputestatistics;Analyzetablecustomerestimatestatisticssample5000rows;。表中设置并行度、表分区优化器模式rule模式。总忽略CBO和统计信息而基于规则choose模式。Oracle根据情况选择ruleorfirst_rowsorall_rowsfirst_rows模式。基于成本,以最快的速度返回记录,会造成总体查询速度的下降或消耗更多的资源,倾向索引扫描,适合OLTP系统all_rows模式。基于成本,确保总体查询时间最短,倾向并行全表扫描例如:Selectlast_namefromcustomerorderbylast_name;用first_rows时,迅速返回记录,但I/O量大,用all_rows时,返回记录慢,但使用资源少。调整SQL表访问全表扫描。返回记录:未排序表>40%,排序表>7%,建议采用并行机制来提高访问速度,DDS;索引访问。最常用的方法,包括索引唯一扫描和索引范围扫描,OLTP;快速完全索引扫描
。访问索引中所有数据块,结果相当于全表扫描,可以用索引扫描代替全表扫描,例如:Selectserv_id,count(*)fromtg_cdr01groupbyserv_id;评估全表扫描的合法性如何实现并行扫描。永久并行化(不推荐)altertablecustomerparalleldegree8;。单个查询并行化select/*+full(emp)parallel(emp,8)*/*fromemp;分区表效果明显优化SQL语句排序排序的操作:排序的操作。orderby子句。groupby子句。selectdistinct子句。创建索引时。union或minus。排序合并连接如何避免排序。添加索引。在索引中使用distinct子句。避免排序合并连接使用提示进行调整使用提示的原则。语法:/*+hint*/。使用表别名:select/*+index(edept_idx)*/*fromempe。检验提示常用的提示。rule。all_rows。first_rows。use_nl
。use_hash。use_merge。index。index_asc。no_index。index_desc(常用于使用max内置函数)。index_combine(强制使用位图索引)。index_ffs(索引快速完全扫描)。use_concat(将查询中所有or条件使用unionall)。parallel。noparallel。full。ordered(基于成本)调整表连接表连接的类型。等连接where条件中用等式连接;。外部连接(左、右连接)在where条件子句的等式谓词放置一个(+)来实现,例如:selecta.ename,bmfromempa,bonusbwherea.ename=b.ename(+);该语句返回所有emp表的记录;。自连接Selecta.valuetotal,B.valuehard,(A.value-b.value)soft,Round((b.value/a.value)*100,1)percFromv$sysstata,v$sysstatbWherea.statistic#=179andB.statistic#=180;反连接反连接常用于notinornotexists中,是指在查询中找到的任何记录都不包含在结果集中的子查询;不建议使用notinornotexists;。半连接查询中使用exists,含义:即使在子查询中返回多条重复的记录,外部查询也只返回一条记录。嵌套循环连接。被连接表中存在索引的情况下使用;。使用use_nl。
hash连接。Hash连接将驱动表加载在内存中,并使用hash技术连接第二个表,提高等连接速度。。适合于大表和小表连接;。使用use_hash。排序合并连接。排序合并连接不使用索引。使用原则:连接表子段中不存在可用索引;查询返回两个表中大部分的数据快;CBO认为全表扫描比索引扫描执行的更快。。使用use_merge使用临时/中间表多个大表关联时,可以分别把满足条件的结果集存放到中间表,然后用中间表关联;SQL子查询的调整关联与非关联子查询。关联:子查询的内部引用的是外部表,每行执行一次;。非关联:子查询只执行一次,存放在内存中。调整notin和notexists语句。可以使用外部连接优化notin子句,例如:selectenamefromempwheredept_nonotin(selectdept_nofromdeptwheredept_name=‘Math’);改为:selectenamefromemp,deptwhereemp.dept_no=dept.dept_noanddept.dept_nameisnull;使用索引调整SQLOracle为什么不使用索引。检查被索引的列或组合索引的首列是否出现在PL/SQL语句的WHERE子句中,这是“执行计划”能用到相关索引的必要条件。
。看采用了哪种类型的连接方式。ORACLE的共有SortMergeJoin(SMJ)、HashJoin(HJ)和NestedLoopJoin(NL)。在两张表连接,且内表的目标列上建有索引时,只有NestedLoop才能有效地利用到该索引。SMJ即使相关列上建有索引,最多只能因索引的存在,避免数据排序过程。HJ由于须做HASH运算,索引的存在对数据查询速度几乎没有影响。。看连接顺序是否允许使用相关索引。假设表emp的deptno列上有索引,表dept的列deptno上无索引,WHERE语句有emp.deptno=dept.deptno条件。在做NL连接时,emp做为外表,先被访问,由于连接机制原因,外表的数据访问方式是全表扫描,emp.deptno上的索引显然是用不上,最多在其上做索引全扫描或索引快速全扫描。。是否用到系统数据字典表或视图。由于系统数据字典表都未被分析过,可能导致极差的“执行计划”。但是不要擅自对数据字典表做分析,否则可能导致死锁,或系统性能下降。。索引列是否函数的参数。如是,索引在查询时用不上。。是否存在潜在的数据类型转换。如将字符型数据与数值型数据比较,ORACLE会自动将字符型用to_number()函数进行转换,从而导致上一种现象的发生。。是否为表和相关的索引搜集足够的统计数据。对数据经常有增、删、改的表最好定期对表和索引进行分析,可用SQL语句“analyzetablexxxxcomputestatisticsforallindexes;”。ORACLE掌握了充分反映实际的统计数据,才有可能做出正确的选择。。索引列的选择性不高。我们假设典型情况,有表emp,共有一百万行数据,但其中的emp.deptno列,数据只有4种不同的值,如10、20、30、40。虽然emp数据行有很多,ORACLE缺省认定表中列的值是在所有数据行均匀分布的,也就是说每种deptno值各有25万数据行与之对应。假设SQL搜索条件DEPTNO=10,利用deptno列上的索引进行数据搜索效率,往往不比全表扫描的高。。索引列值是否可为空(NULL)。如果索引列值可以是空值,在SQL语句中那些要返回NULL值的操作,将不会用到索引,如COUNT(*),而是用全表扫描。这是因为索引中存储值不能为全空。。看是否有用到并行查询(PQO)。并行查询将不会用到索引。。如果从以上几个方面都查不出原因的话,我们只好用采用在语句中加hint的方式强制ORACLE使用最优的“执行计划”。hint采用注释的方式,有行注释和段注释两种方式。如我们想要用到A表的IND_COL1索引的话,可采用以下方式:“SELECT/*+INDEX(AIND_COL1)*/*FROMAWHERECOL1=XXX;"如何屏蔽索引语句的执行计划中有不良索引时,可以人为地屏蔽该索引,方法:。数值型:在索引字段上加0,例如select*fromempwhereemp_no+0=v_emp_no;。字符型:在索引字段上加‘’,例如
select*fromtg_cdr01wheremsisdn||’’=v_msisdn;
第3章SQL语句处理的过程在调整之前我们需要了解一些背景知识,只有知道这些背景知识,我们才能更好的去调整sql语句。本节介绍了SQL语句处理的基本过程,主要包括:查询语句处理DML语句处理(insert,update,delete)DDL语句处理(create..,drop..,alter..,)事务控制(commit,rollback)语句的执行过程(SQLSQL语句的执行过程(SQLStatementExecution)在某些情况下,Oracle运行sql的过程可能与下面列出的各个阶段的顺序有所不同。如DEFINE阶段可能在FETCH阶段之前,这主要依赖你如何书写代码。对许多oracle的工具来说,其中某些阶段会自动执行。绝大多数用户不需要关心各个阶段的细节问题,然而,知道执行的各个阶段还是有必要的,这会帮助你写出更高效的SQL语句来,而且还可以让你猜测出性能差的SQL语句主要是由于哪一个阶段造成的,然后我们针对这个具体的阶段,找出解决的办法。DML语句的处理本节给出一个例子来说明在DML语句处理的各个阶段到底发生了什么事情。假设你使用Pro*C程序来为指定部门的所有职员增加工资。程序已经连到正确的用户,你可以在你的程序中嵌入如下的SQL语句:EXECSQLUPDATEemployeesSETsalary=1.10*salaryWHEREdepartment_id=:var_department_id;var_department_id是程序变量,里面包含部门号,我们要修改该部门的职员的工资。当这个SQL语句执行时,使用该变量的值。每种类型的语句都需要如下阶段:第1步:CreateaCursor创建游标第2步:ParsetheStatement分析语句第5步:BindAnyVariables绑定变量第7步:RuntheStatement运行语句第9步:ClosetheCursor关闭游标如果使用了并行功能,还会包含下面这个阶段:第6步:ParallelizetheStatement并行执行语句如果是查询语句,则需要以下几个额外的步骤,如图3所示:第3步:DescribeResultsofaQuery描述查询的结果集第4步:DefineOutputofaQuery定义查询的输出数据第8步:FetchRowsofaQuery取查询出来的行下面具体说一下每一步中都发生了什么事情:.创建游标(Create第1步:创建游标(CreateaCursor)由程序接口调用创建一个游标(cursor)。任何SQL语句都会创建它,特别在运行DML语句时,都是自动创建游标的,不需要开发人员干预。多数应用中,游标的创建是自动的。然而,在预编译程序
(pro*c)中游标的创建,可能是隐含的,也可能显式的创建。在存储过程中也是这样的。分析语句(Parse第2步:分析语句(ParsetheStatement)在语法分析期间,SQL语句从用户进程传送到Oracle,SQL语句经语法分析后,SQL语句本身与分析的信息都被装入到共享SQL区。在该阶段中,可以解决许多类型的错误。语法分析分别执行下列操作:*翻译SQL语句,验证它是合法的语句,即书写正确*实现数据字典的查找,以验证是否符合表和列的定义*在所要求的对象上获取语法分析锁,使得在语句的语法分析过程中不改变这些对象的定义*验证为存取所涉及的模式对象所需的权限是否满足*决定此语句最佳的执行计划*将它装入共享SQL区*对分布的语句来说,把语句的全部或部分路由到包含所涉及数据的远程节点以上任何一步出现错误,都将导致语句报错,中止执行。只有在共享池中不存在等价SQL语句的情况下,才对SQL语句作语法分析。在这种情况下,数据库内核重新为该语句分配新的共享SQL区,并对语句进行语法分析。进行语法分析需要耗费较多的资源,所以要尽量避免进行语法分析,这是优化的技巧之一。语法分析阶段包含了不管此语句将执行多少次,而只需分析一次的处理要求。Oracle只对每个SQL语句翻译一次,在以后再次执行该语句时,只要该语句还在共享SQL区中,就可以避免对该语句重新进行语法分析,也就是此时可以直接使用其对应的执行计划对数据进行存取。这主要是通过绑定变量(bindvariable)实现的,也就是我们常说的共享SQL,后面会给出共享SQL的概念。虽然语法分析验证了SQL语句的正确性,但语法分析只能识别在SQL语句执行之前所能发现的错误(如书写错误、权限不足等)。因此,有些错误通过语法分析是抓不到的。例如,在数据转换中的错误或在数据中的错(如企图在主键中插入重复的值)以及死锁等均是只有在语句执行阶段期间才能遇到和报告的错误或情况。查询语句的处理查询与其它类型的SQL语句不同,因为在成功执行后作为结果将返回数据。其它语句只是简单地返回成功或失败,而查询则能返回一行或许多行数据。查询的结果均采用表格形式,结果行被一次一行或者批量地被检索出来。从这里我们可以得知批量的fetch数据可以降低网络开销,所以批量的fetch也是优化的技巧之一。有些问题只与查询处理相关,查询不仅仅指SELECT语句,同样也包括在其它SQL语句中的隐含查询。例如,下面的每个语句都需要把查询作为它执行的一部分:INSERTINTOtableSELECT...UPDATEtableSETx=yWHERE...DELETEFROMtableWHERE...CREATEtableASSELECT...具体来说,查询要求读一致性可能使用回滚段作中间处理可能要求SQL语句处理描述、定义和取数据阶段描述查询结果(Describe第3步:描述查询结果(DescribeResultsofaQuery)
描述阶段只有在查询结果的各个列是未知时才需要;例如,当查询由用户交互地输入需要输出的列名。在这种情况要用描述阶段来决定查询结果的特征(数据类型,长度和名字)。定义查询的输出数据(Define第4步:定义查询的输出数据(DefineOutputofaQuery)在查询的定义阶段,你指定与查询出的列值对应的接收变量的位置、大小和数据类型,这样我们通过接收变量就可以得到查询结果。如果必要的话,Oracle会自动实现数据类型的转换。这是将接收变量的类型与对应的列类型相比较决定的。绑定变量(Bind第5步:绑定变量(BindAnyVariables)此时,Oracle知道了SQL语句的意思,但仍没有足够的信息用于执行该语句。Oracle需要得到在语句中列出的所有变量的值。在该例中,Oracle需要得到对department_id列进行限定的值。得到这个值的过程就叫绑定变量(bindingvariables)此过程称之为将变量值捆绑进来。程序必须指出可以找到该数值的变量名(该变量被称为捆绑变量,变量名实质上是一个内存地址,相当于指针)。应用的最终用户可能并没有发觉他们正在指定捆绑变量,因为Oracle的程序可能只是简单地指示他们输入新的值,其实这一切都在程序中自动做了。因为你指定了变量名,在你再次执行之前无须重新捆绑变量。你可以改变绑定变量的值,而Oracle在每次执行时,仅仅使用内存地址来查找此值。如果Oracle需要实现自动数据类型转换的话(除非它们是隐含的或缺省的),你还必须对每个值指定数据类型和长度。关于这些信息可以参考oracle的相关文档,如OracleCallInterfaceProgrammer'sGuide并行执行语句(Parallelize第6步:并行执行语句(ParallelizetheStatement)ORACLE可以在SELECTs,INSERTs,UPDATEs,MERGEs,DELETEs语句中执行相应并行查询操作,对于某些DDL操作,如创建索引、用子查询创建表、在分区表上的操作,也可以执行并行操作。并行化可以导致多个服务器进程(oracleserverprocesses)为同一个SQL语句工作,使该SQL语句可以快速完成,但是会耗费更多的资源,所以除非很有必要,否则不要使用并行查询。执行语句(Run第7步:执行语句(RuntheStatement)到了现在这个时候,Oracle拥有所有需要的信息与资源,因此可以真正运行SQL语句了。如果该语句为SELECT查询或INSERT语句,则不需要锁定任何行,因为没有数据需要被改变。然而,如果语句为UPDATE或DELETE语句,则该语句影响的所有行都被锁定,防止该用户提交或回滚之前,别的用户对这些数据进行修改。这保证了数据的一致性。对于某些语句,你可以指定执行的次数,这称为批处理(arrayprocessing)。指定执行N次,则绑定变量与定义变量被定义为大小为N的数组的开始位置,这种方法可以减少网络开销,也是优化的技巧之一。取出查询的行(Fetch第8步:取出查询的行(FetchRowsofaQuery)在fetch阶段,行数据被取出来,每个后续的存取操作检索结果集中的下一行数据,直到最后一行被取出来。上面提到过,批量的fetch是优化的技巧之一。关闭游标(Close第9步:关闭游标(ClosetheCursor)SQL语句处理的最后一个阶段就是关闭游标语句的处理(DDLProcessing)DDL语句的处理(DDLStatementProcessing)
DDL语句的执行不同与DML语句和查询语句的执行,这是因为DDL语句执行成功后需要对数据字典数据进行修改。对于DDL语句,语句的分析阶段实际上包括分析、查找数据字典信息和执行。事务管理语句、会话管理语句、系统管理语句只有分析与执行阶段,为了重新执行该语句,会重新分析与执行该语句。事务控制(Control事务控制(ControlofTransactions)一般来说,只有使用ORACLE编程接口的应用设计人员才关心操作的类型,并把相关的操作组织在一起,形成一个事务。一般来说,我门必须定义事务,这样在一个逻辑单元中的所有工作可以同时被提交或回滚,保证了数据的一致性。一个事务应该由逻辑单元中的所有必须部分组成,不应该多一个,也不应该少一个。在事务开始和结束的这段时间内,所有被引用表中的数据都应该在一致的状态(或可以被回溯到一致的状态)事务应该只包含可以对数据进行一致更改(oneconsistentchangetothedata)的SQL语句例如,在两个帐号之间的转帐(这是一个事务或逻辑工作单元),应该包含从一个帐号中借钱(由一个SQL完成),然后将借的钱存入另一个帐号(由另一个SQL完成)。这2个操作作为一个逻辑单元,应该同时成功或同时失败。其它不相关的操作,如向一个帐户中存钱,不应该包含在这个转帐事务中。在设计应用时,除了需要决定哪种类型的操作组成一个事务外,还需要决定使用BEGIN_DISCRETE_TRANSACTIO存储过程是否对提高小的、非分布式的事务的性能有作用。