一、优化器基本知识
Oracle在执行一个SQL之前,首先要分析一下语句的执行计划,然后再按执行计划去执行。分析语句的执行计划的工作是由优化器(Optimizer)来完成的。不同的情况,一条SQL可能有多种执行计划,但在某一时点,一定只有一种执行计划是最优的,花费时间是最少的。
相信你一定会用Pl/sql Developer、Toad等工具去看一个语句的执行计划,不过你可能对Rule、Choose、First rows、All rows这几项有疑问,因为我当初也是这样的,那时我也疑惑为什么选了以上的不同的项,执行计划就变了?
1、优化器的优化方式
Oracle的优化器优化方式:
RBO方式
即基于规则的优化方式(Rule-Based Optimization,简称为RBO)。优化器在分析SQL语句时,所遵循的是Oracle内部预定的一些规则。比如我们常见的,当一个where子句中的一列有索引时去走索引。
CBO方式
基于代价的优化方式(Cost-Based Optimization,简称为CBO)。依词义可知,它是看语句的代价(Cost)了,这里的代价主要指Cpu和内存。优化器在判断是否用这种方式时,主要参照的是表及索引的统计信息。统计信息给出表的大小、有少行、每行的长度等信息。这些统计信息起初在库内是没有的,是你在做analyze后才出现的,很多的时侯过期统计信息会令优化器做出一个错误的执行计划,因些我们应及时更新这些信息。在Oracle8及以后的版本,Oracle列推荐用CBO的方式。
我们要明了,不一定走索引就是优的 ,比如一个表只有两行数据,一次IO就可以完成全表的检索,而此时走索引时则需要两次IO,这时对这个表做全表扫描(full table scan)是最好的。
新版本的oracle逐渐抛弃对Rule方式的支持,即使是Rule方式,最后sql执行效率的衡量标准都是,sql执行消耗了多少资源?对代价(COST)的优化方式,需要表,索引的统计信息,需要每天多表和索引进行定时的分析,但是统计信息也是历史的,有时候也不一定是最优的,统计信息等于就是一个人的经验,根据以前的经验来判断sql该怎么执行(得到优化的sql执行路径),所以具体优化执行的时候,先手工分析sql,看是用RBO方式消耗大,还是CBO消耗大;DBA的工作就是要根据当前oracle的运行日志,进行各种调整,使当前的oracle运行效率尽量达到最优.可以在运行期间,采用hint灵活地采用优化方式.
CHOOSE (选择性)
设置缺省的Oracle优化器,可以通过对init.ora文件中OPTIMIZER_MODE参数的各种声明,如RULE,COST,CHOOSE,ALL_ROWS,FIRST_ROWS . 你当然也在SQL句级或是会话(session)级对其进行覆盖。
为了使用基于成本的优化器(CBO, Cost-Based Optimizer) ,你必须经常运行analyze 命令,以增加数据库中的对象统计信息(object statistics)的准确性。
如果数据库的优化器模式设置为选择性(CHOOSE),那么实际的Oracle优化器模式将和是否运行过analyze命令有关。如果table已经被analyze过, 优化器模式将自动成为CBO , 反之,数据库将采用RULE形式的优化器。
在缺省情况下,Oracle采用CHOOSE优化器,为了避免那些不必要的全表扫描(full table scan) ,你必须尽量避免使用CHOOSE优化器,而直接采用基于规则或者基于成本的Oracle优化器。
2、优化器的优化模式(Optermizer Mode)
优化模式包括Rule,Choose,First rows,All rows这四种方式,也就是我们以上所提及的。如下我解释一下:
Rule:不用多说,即走基于规则的方式。 (RBO优化方式)
Choolse:这是我们应观注的,默认的情况下Oracle用的便是这种方式。指的是当一个表或或索引有统计信息,则走CBO的方式,如果表或索引没统计信息,表又不是特别的小,而且相应的列有索引时,那么就走索引,走RBO的方式。
在缺省情况下,ORACLE采用CHOOSE优化器,为了避免那些不必要的全表扫描(full table scan),你必须尽量避免使用CHOOSE优化器,而直接采用基于规则或者基于成本的优化器。
First Rows:它与Choose方式是类似的,所不同的是当一个表有统计信息时,它将是以最快的方式返回查询的最先的几行,从总体上减少了响应时间。 (CBO优化方式,提供一个最快的反应时间,根据系统的需求,使用情况)
All Rows:也就是我们所说的Cost的方式,当一个表有统计信息时,它将以最快的方式返回表的所有的行,从总体上提高查询的吞吐量。没有统计信息则走基于规则的方式。 (CBO优化方式,提供最大的吞吐量,就是使执行总量达到最大) [Page]
First Rows和All Rows是有冲突的.如果想最快第返回给用户,就不可能传递更多的结果,这就是First Rows返回最先检索到的行(或记录);而All Rows是为了尽量将所有的结果返回给用户,由于量大,用户就不会很快得到返回结果.就象空车能跑得很快,重装车只能慢慢地跑;
3、如何设定选用哪种优化模式
A、Instance级别
我们可以通过在init<SID>.ora文件中设定OPTIMIZER_MODE=RULE、OPTIMIZER_MODE=CHOOSE、OPTIMIZER_MODE=FIRST_ROWS、OPTIMIZER_MODE=ALL_ROWS去选用3所提的四种方式,如果你没设定OPTIMIZER_MODE参数则默认用的是Choose这种方式。
init.ora和init<SID>.ora都在$ORACLE_HOME/dbs目录下,可以用find $ORACLE_HOME -name init*.ora查看该目录下的init文件.
init.ora是对全体实例有效的;init<SID>.ora只对指定的实例有效.
B、Sessions级别
通过SQL> ALTER SESSION SET OPTIMIZER_MODE=<Mode>;来设定。将覆盖init.ora,init<sid>.ora设定的优化模式,也可以在sql语句中采用hint强制选定优化模式.如下:
C、语句级别
这些需要用到Hint,比如:
SQL> SELECT a.userid,
2 b.name,
3 b.depart_name
4 FROM tf_f_yhda a,
5 tf_f_depart b
6 WHERE a.userid=b.userid;
在这儿采用hint,强制采用基于规则(rule)的优化模式;
hint语法,结尾,中间填写强制采用的优化模式.
4、为什么有时一个表的某个字段明明有索引,当观察一些语的执行计划确不走索引呢?如何解决呢?
A、不走索引大体有以下几个原因
♀你在Instance级别所用的是all_rows的方式
♀你的表的统计信息(最可能的原因)
♀你的表很小,上文提到过的,Oracle的优化器认为不值得走索引。
B、解决方法
♀可以修改init<SID>.ora中的OPTIMIZER_MODE这个参数,把它改为Rule或Choose,重起数据库。也可以使用4中所提的Hint.
♀删除统计信息
SQL>analyze table table_name delete statistics;
♀表小不走索引是对的,不用调的。
不走索引的其它原因:
1、建立组合索引,但查询谓词并未使用组合索引的第一列,此处有一个INDEX SKIP SCAN概念。
2、在包含有null值的table列上建立索引,当时使用select count(*) from table时不会使用索引。
3、在索引列上使用函数时不会使用索引,如果一定要使用索引只能建立函数索引。
4、当被索引的列进行隐式的类型转换时不会使用索引。如:select * from t where indexed_column = 5,而indexed_column列建立索引但类型是字符型,这时Oracle会产生隐式的类型转换,转换后的语句类似于select * from t where to_number(indexed_column) = 5,此时不走索引的情况类似于case3。日期转换也有类似问题,如: select * from t where trunc(date_col) = trunc(sysdate)其中date_col为索引列,这样写不会走索引,可改写成select * from t where date_col >= trunc(sysdate) and date_col < trunc(sysdate+1),此查询会走索引。
5、并不是所有情况使用索引都会加快查询速度,full scan table 有时会更快,尤其是当查询的数据量占整个表的比重较大时,因为full scan table采用的是多块读,当Oracle优化器没有选择使用索引时不要立即强制使用,要充分证明使用索引确实查询更快时再使用强制索引。
6、<>
7、like’?’百分号在前。
5、其它相关
A、如何看一个表或索引是否是统计信息
SQL>SELECT * FROM user_tables 2 WHERE table_name=<table_name>
3 AND num_rows is not null;
SQL>SELECT * FROM user_indexes
2 WHERE table_name=<table_name>
3 AND num_rows is not null;
b、如果我们先用CBO的方式,我们应及时去更新表和索引的统计信息,以免生形不切合实的执行计划。
SQL> ANALYZE TABLE table_name COMPUTE STATISTICS;
SQL> ANALYZE INDEX index_name ESTIMATE STATISTICS;
二、优化器的二十六个参数
影响系统性能类可变参数
(1)CHECKPOINT_PROCESS该参数根据是否要求检查点而设置成TRUE或者FALSE。当所有缓冲区的信息写到磁盘时,检查点进程(CHPT)建立一个静态的点。在归档日志文件中做一个记号表示有一个检查点发生。检查点发生在归档日志转换的时候或当达到log_checkpoint_interval定义的块数的时候。当设置此参数为TRUE时,后台进程CHPT便可工作。在检查点期间内,若日志写进程(LGWR)的性能减低,则可用CHPT进程加以改善。
(2)DB_BLOCK_CHECKPOINT_BATCH该参数的值设置得较大时,可加速检查点的完成。当指定的值比参数DB_BLOCK_CHECKPOINT_BATCH大时,其效果和指定最大值相同。
(3)DB_BLOCK_BUFFERS该参数是在SGA中可作缓冲用的数据库块数。该参数决定SGA的大小,对数据库性能具有决定性因素。若取较大的值,则可减少I/O次数,但要求内存空间较大。每个缓冲区的大小由参数DB_BLOCK_SIZE决定。
(4)DB_BLOCK_SIZE该参数表示Oracle数据库块的大小,以字节为单位,典型值为2048或4096。该值一旦设定则不能改变。它影响表及索引的FREELISTS参数的最大值。
(5)DB_FILES该参数为数据库运行时可打开的数据文件最大数目。
(6)DB_FILE_MULTIBLOCK_READ_COUNT该参数表示在顺序扫描时一次I/O操作可读的最大块数,该最大块数取决于操作系统,其值在4至16或者32是比较好。
(7)D1SCRETE_TRANSACTION_ENABLED该参数实现一个更简单、更快的回滚机制,以改进某些事务类型的性能。当设置为TRUE时,可改善某些类型的事务性能。
(8)LOG_ARCHIVE_BUFFER_SIZE此参数的值依赖于操作系统,它与LOG_ARCHIVE_BUFFER 参数一起用于调整有归档日志的运行,使其运行速度尽量加快,但不能快到降低性能。仅当直接归档到磁带设备时才需要增加这些参数的值,重做日志缓冲区要等待归档日志缓冲区变得可用。
(9)LOG_ARCHIVE_BUFFER该参数指定用于归档的日志时的缓冲区数。
(10)LOG_BUFFER该参数指明分配给SGA中的日志缓冲区的字节数,该参数值较大时,可减少日志I/O的次数。对于繁忙的系统不宜采用大于或等于64K的值。缺省值-般为数据库块的4倍。
(11)LOG_CHECKPOINT_TIMEOUT该参数指明两个检查点之间的时间间隔,若指定为0时,则说明不允许进行基于时间的检查点。
(12)LOG_CHECKPOINT_INTERVAL该参数用来确定检查点进程的执行频率。这个数值设置成取检查点之前处理的重做缓冲区块的数量。
(13)LOG_FILES该参数指定运行期间数据库可打开的日志文件数。若需要较大的SGA空间,而不需多个日志文件,则可减少该值。
(14)LOG_SIMULTANEOUS_COPIES该参数是日志缓冲区副本闩锁的最大数,为同时写日志项所用。为提高性能,可设置此参数为两倍的CPU数,对单进程系统,该值多数设置为0,此时断开闩锁。
(15)LOG_SMALL_ENTRY_MAX_SIZE该参数与LOG_SIMULTANEOUS_COPIES参数配合使用。若日志项大于此项,则在给缓冲区分配空间并获得日志复制闩锁之后,用户进程释放日志复制闩锁。
(16)OPTIMIZRER_MODE若该参数的值为RULE,则Oracle优化器选择基于规则的优化;若设置为COST,并且在数据字典中存在有统计信息,则Oracle优化器选择基于代价的优化方法进行优化。
(17)SEQUENCE_CACHE_ENTRIES该参数指明在SGA中可进行高速缓存的序列数,用于直接存取。该高速缓存区是基于最近最少使用(LRU)的算法进行管理的。若此值设置得较高,则可达到较高的并发性。
(18)SEQUENCE_CACHE_HASH_BUCKETS该参数用于加速查看高速缓冲区最近请求的最新序列的桶式地址数,每个桶式地址占8个字节。高速缓冲区以散列表排列,该参数应为质数。
(19)SERIALIZEABLE此参数用于保证重复读的一致性。当它设置为TRUE时,查询可保证表级读一致,以防止在此查询提交之前的修改。
(20)SHARED_POOL_SIZE该参数指定共享池的大小,其中包括共享光标及存储过程。在多用户系统中,较大的SHARED_POOL_SIZE值可改善SQL语句的执行性能,但较小的值可节省内存。
(21)SMALL_TABLE_THRESHOLD该参数决定SGA中用于扫描的缓冲区的数目,若表的数目小于该值,则该表可整个地读入高速缓存区。若表大于该值,则立即重用该缓冲区。一般用缺省值可使性能最好。
(22)SORT_AREA_TETAINED_SIZE这是会话内存的最大数量,用于内存排序。当从排序空间提出最后-行时,便释放该内存。若排序要较大的内存,则分配一临时段,排序便可在盘上进行。用于排序的最大总量可由SORT_AREA_SIZE指定,而不用此参数。可以分配同样大小的多个排序空间,不过一般对于复杂的查询才需要。
(23)SORT_AREA_SIZE该参数用于指定进行外排序(磁盘)时所需PGA内存的最大数量,以字节为单位。当排序行写入磁盘时,该内存被释放。增大该参数的值,可改进排序效率。一般不调整该参数,除非排序量很大时才调整。
(24)SORT_SPACEMP_SIZE该参数仅在排序量很大时才调整该参数。可用下式设置该参数,使排序能最佳地使用盘空间。
(25)SQLTRACE该参数设置为TRUE时,便可跟踪,以获得改善性能的信息。因为跟踪会增加开销,所以一般仅在收集信息时才置为TRUE。在实际使用时,可用ALTER SESSION命令覆盖它。
(26)TRANSACTION该参数设置并发事务的最大数。若此值较大,则需增加SGA空间和分配的回滚段数量。缺省值大于PROCESS时,可允许递归事务。
Oracle优化器存在两种优化方式:基于规则的优化方式RBO(Rule-Based Optimization)和基于代价的优化方式CBO(Cost-Based Optimization),其中RBO方式在oracle10g中已经摒弃。
RBO是一种基于规则的优化方式,优化器在分析SQL语句时遵循oracle内部预定的一些规则,如,当一个where子句中的一列有索引时就走索引。RBO根据可用的访问路径和访问路径的等级来选择访问计划,等级越高的访问路径运行SQL速度越慢,如果有多个路径,则选用等级较低的路径。
CBO是一个基于代价或成本的优化方式,代价或成本即内存和CPU 的消耗。优化只要是根据可用的访问路径、表或索引的统计信息等来选择一个成本最低的访问路径。所以要及时更新统计信息,避免过期的统计信息让优化器选择一个错误的执行计划。
CBO主要包含查询转化器(Query Transformer)、评估器(Estimator)和计划生成器(Plan Generator)。
一、查询优化器,由于查询SQL语句的形式可能会影响所产生的执行计划,查询转化器就是改变查询语句的形式以获得更为高效的执行计划,Oracle主要提供四种转化技术:
1.视图合并(View Mergin):查询语句含有视图时,会产生独立的“视图查询块”,影响语句的整体性,会产生不良的执行计划。视图合并会去掉“视图查询块”,将视图合并至一个整体的查询块中,使执行计划的整体性得到提升。
2.谓词推进(Predicate Pushing):这项技术是将不能进行合并的视图相应的谓词推进到视图查询块中。这些谓词通常是可索引或有较强过滤性的。
3.非嵌套子查询(Subquery Unnesting):子查询和视图一样,会被放进独立的查询块,查询转换器会将大多数子查询转为连接放进同一个查询块,不能合并的按照一个高效的方式排列。
4.物化视图的查询重写:当query_rewrite_enabled=true时,转化器会寻找与该语句相关联的物化视图,并将该语句改写。
二、评估器,评估器通过计算三个值来评估计划的总成本:选择性(Selectivity)、基数(Cardinality)、成本(Cost)。
1.选择性:是一个大于0小于1的数,0表示没有记录被选定,1表示所有记录都被选定。统计信息和直方图关系到选择性值的准确性。如:name=’Davis’,如果不存在统计信息评估器将根据所用的谓词来指定一个缺省的选择性值,此时评估器会始终认为等式谓词的选择性比不等式谓词小;如果存在统计信息而不存在直方图,此时选择性值为1/count(distinct name);如果存在统计信息也存在直方图,选择性值则为count(name)where name=’Davis’/ count(name)where name is not null。
2.基数:通常表中的行数称为“基础基数”(Base cardinality);当用WHERE中的条件过滤后剩下的行数称为“有效基数”(Effective cardinality);连接操作之后产生的结果集行数称为“连接基数”(Join cardinality);一个字段DISTINCT之后的行数称为“DISTINCT基数”等等。
3.成本:就是度量资源消耗的单位。可以理解为执行表扫描、索引扫描、连接、排序等操作所的消耗I/O、CPU、内存。
三、计划生成器,最后是计划生成器的作用就是生成大量的执行计划,然后选择其中总体成本最低的一个,如果它发现当前执行计划的成本已经很低了,它将停止试验,相反当前计划的成本如果很高,它将继续试验其他执行计划。