Oracle 11g于2007年7月11日美国东部时间11时(北京时间11日22时)正式发布,11g是甲骨文公司30年来发布的最重要的数据库版本,根据用户的需求实现了信息生命周期管理(Information Lifecycle Management)等多项创新。
一、新特性提纲
1、数据库管理部分
◆数据库重演(Database Replay)
这一特性可以捕捉整个数据的负载,并且传递到一个从备份或者standby数据库中创建的测试数据库上,然后重演负责以测试系统调优后的效果。
◆SQL重演(SQL Replay)
和前一特性类似。但是只是捕捉SQL负载部分,而不是全部负载。
◆计划管理(Plan Management)
这一特性允许你将某一特定语句的查询计划固定下来,无论统计数据变化还是数据库版本变化都不会改变她的查询计划。
◆自动诊断知识库(Automatic Diagnostic Repository ADR)
当Oracle探测到重要错误时,会自动创纪一个事件(incident),并且捕捉到和这一事件相关的信息,同时自动进行数据库健康检查并通知DBA。此外,这些信息还可以打包发送给Oracle支持团队。
◆事件打包服务(Incident Packaging Service)
如果你需要进一步测试或者保留相关信息,这一特性可以将与某一事件相关的信息打包。并且你还可以将打包信息发给oracle支持团队。
◆基于特性打补丁(Feature Based Patching)
在打补丁包时,这一特性可以使你很容易区分出补丁包中的那些特性是你正在使用而必须打的。企业管理器(EM)使你能订阅一个基于特性的补丁服务,因此企业管理器可以自动扫描那些你正在使用的特性有补丁可以打。
◆自动SQL优化(Auto SQL Tuning)
10g的自动优化建议器可以将优化建议写在SQL profile中。而在11g中,你可以让oracle自动将能3倍于原有性能的profile应用到SQL语句上。性能比较由维护窗口中一个新管理任务来完成。
◆访问建议器(Access Advisor)
11g的访问建议器可以给出分区建议,包括对新的间隔分区(interval partitioning)的建议。间隔分区相当于范围分区(range partitioning)的自动化版本,她可以在必要时自动创建一个相同大小的分区。范围分区和间隔分区可以同时存在于一张表中,并且范围分区可以转换为间隔分区。
◆自动内存优化(Auto Memory Tuning)
在9i中,引入了自动PGA优化;10g中,又引入了自动SGA优化。到了11g,所有内存可以通过只设定一个参数来实现全表自动优化。你只要告诉oracle有多少内存可用,她就可以自动指定多少内存分配给PGA、多少内存分配给SGA和多少内存分配给操作系统进程。当然也可以设定最大、最小阈值。
◆资源管理器(Resource Manager)
11g的资源管理器不仅可以管理CPU,还可以管理IO。你可以设置特定文件的优先级、文件类型和ASM磁盘组。
◆ADDM
ADDM在10g被引入。11g中,ADDM不仅可以给单个实例建议,还可以对整个RAC(即数据库级别)给出建议。另外,还可以将一些指示(directive)加入ADDM,使之忽略一些你不关心的信息。
◆AWR 基线(AWR Baselines)
AWR基线得到了扩展。可以为一些其他使用到的特性自动创建基线。默认会创建周基线。
2、PLSQL部分
◆结果集缓存(Result Set Caching)
这一特性能大大提高很多程序的性能。在一些MIS系统或者OLAP系统中,需要使用到很多"select count(*)"这样的查询。在之前,我们如果要提高这样的查询的性能,可能需要使用物化视图或者查询重写的技术。在11g,我们就只需要加一个/*+result_cache*/的提示就可以将结果集缓存住,这样就能大大提高查询性能。当然,在这种情况下,我们可能还要关心另外一个问题:完整性。因为在oracle中是通过一致性读来保证数据的完整性的。而显然,在这种新特性下,为提高性能,是从缓存中的结果集中读取数据,而不会从回滚段中读取数据的。关于这个问题,答案是完全能保证完整性。因为结果集是被独立缓存的,在查询期间,任何其他DML语句都不会影响结果集中的内容,因而可以保证数据的完整性。
◆对象依赖性改进
在11g之前,如果有函数或者视图依赖于某张表,一旦这张表发生结构变化,无论是否涉及到函数或视图所依赖的属性,都会使函数或视图变为invalid。在11g中,对这种情况进行了调整:如果表改变的属性与相关的函数或视图无关,则相关对象状态不会发生变化。
◆正则表达式的改进
在10g中,引入了正则表达式。这一特性大大方便了开发人员。11g,oracle再次对这一特性进行了改进。其中,增加了一个名为regexp_count的函数。另外,其他的正则表达式函数也得到了改进。
◆新SQL语法 =>
我们在调用某一函数时,可以通过=>来为特定的函数参数指定数据。而在11g中,这一语法也同样可以出现在sql语句中了。例如,你可以写这样的语句:
select f(x=>6) from dual;
◆对TCP包(utl_tcp、utl_smtp…)支持FGAC(Fine Grained Access Control)安全控制
◆增加了只读表(read-only table)
在以前,我们是通过触发器或者约束来实现对表的只读控制。11g中不需要这么麻烦了,可以直接指定表为只读表。
◆触发器执行效率提高了
内部单元内联(Intra-Unit inlining)
在C语言中,你可以通过内联函数(inline)或者宏实现使某些小的、被频繁调用的函数内联,编译后,调用内联函数的部分会编译成内联函数的函数体,因而提高函数效率。在11g的plsql中,也同样可以实现这样的内联函数了。
◆设置触发器顺序
可能在一张表上存在多个触发器。在11g中,你可以指定它们的触发顺序,而不必担心顺序混乱导致数据混乱。
◆混合触发器(compound trigger)
这是11g中新出现的一种触发器。她可以让你在同一触发器中同时具有申明部分、before过程部分、after each row过程部分和after过程部分。
◆创建无效触发器(Disabled Trigger)
11g中,开发人员可以可以闲创建一个invalid触发器,需要时再编译她。
◆在非DML语句中使用序列(sequence)
在之前版本,如果要将sequence的值赋给变量,需要通过类似以下语句实现:
select seq_x.next_val into v_x from dual;
在11g中,不需要这么麻烦了,下面语句就可以实现:
v_x := seq_x.next_val;
◆PLSQL_Warning
11g中,可以通过设置PLSQL_Warning=enable all,如果在"when others"没有错误爆出就发警告信息。
◆PLSQL的可继承性
可以在oracle对象类型中通过super(和java中类似)关键字来实现继承性。
◆编译速度提高
因为不在使用外部C编译器了,因此编译速度提高了。
◆改进了DBMS_SQL包
其中的改进之一就是DBMS_SQL可以接收大于32k的CLOB了。另外还能支持用户自定义类型和bulk操作。
◆增加了continue关键字
在PLSQL的循环语句中可以使用continue关键字了(功能和其他高级语言中的continue关键字相同)。
◆新的PLSQL数据类型――simple_integer
这是一个比pls_integer效率更高的整数数据类型。
3、其他部分
◆增强的压缩技术
可以最多压缩2/3的空间。
◆高速推进技术
可以大大提高对文件系统的数据读取速度。
◆增强了DATA Guard
可以创建standby数据库的快照,用于测试。结合数据库重演技术,可以实现模拟生成系统负载的压力测试。
◆在线应用升级
也就是热补丁――安装升级或打补丁不需要重启数据库。
◆数据库修复建议器
可以在错误诊断和解决方案实施过程中指导DBA。
◆逻辑对象分区
可以对逻辑对象进行分区,并且可以自动创建分区以方便管理超大数据库(Very Large Databases VLDBs)。
◆新的高性能的LOB基础结构
◆新的PHP驱动
二、详细介绍
1、分区
Partition(分区)一直是Oracle数据库引以为傲的一项技术,正是分区的存在让Oracle高效的处理海量数据成为可能,在Oracle 11g中,分区技术在易用性和可扩展性上再次得到了增强。
1.1. Interval Partitioning
在我曾经的一个项目中,由于数据量的巨大,所以表设计为每一个小时一个分区,数据库管理员日常要做的一件重复而无聊的工作就是每隔一天要生成新的24个分区,用以存储第二天的数据。而在11g中这项工作可以交由Oracle自动完成了,基于Range和List的Interval Partitioning分区类型登场。
CREATE TABLE TB_INTERVAL
PARTITION BY RANGE (time_col)
INTERVAL(NUMTOYMINTERVAL(1, 'month'))
(PARTITION P0 VALUES LESS THAN (TO_DATE('1-1-2010', 'dd-mm-yyyy')));
指定需要Oracle自动创建分区的间隔时间,上面这个例子是1个月,然后至少创建一个基本分区,上面这个例子是在2010-1-1之前的所有数据都在P0分区中,以后每个月的数据都会存放在Oracle自动创建的一个新分区中。
1.2. System Partitioning
系统分区,在这个新的类型中,我们不需要指定任何分区键,数据会进入哪个分区完全由应用程序决定,实际上也就是由SQL来决定,终于,我们在Insert语句中可以指定插入哪个分区了。
假设我们创建了下面这张分区表,注意,没有指定任何分区键:
CREATE TABLE systab (c1 integer, c2 integer)
PARTITION BY SYSTEM
(
PARTITION p1 TABLESPACE tbs_1,
PARTITION p2 TABLESPACE tbs_2,
PARTITION p3 TABLESPACE tbs_3,
PARTITION p4 TABLESPACE tbs_4
);
现在由SQL语句来指定插入哪个分区:
-- 数据插入p1分区
INSERT INTO systab PARTITION (p1) VALUES (4,5);
-- 数据插入第2个分区,也就是p2分区
INSERT INTO systab PARTITION (2) VALUES (7,8);
-- 为了实现绑定变量,用pno变量来代替实际分区号,以避免过度解析
INSERT INTO systab PARTITION (:pno) VALUES (9,10);
由于System Partitioning的特殊性,所以很明显,这种类型的分区将不支持Partition Split操作,也不支持create table as select操作。
1.3. More Composite Partitioning
在10g中,我们知道复合分区只支持Range-List和Range-Hash,而在在11g中复合分区的类型大大增加,现在Range,List,Interval都可以作为Top level分区,而Second level则可以是Range,List,Hash,也就是在11g中可以有3*3=9种复合分区,满足更多的业务需求。
1.4. Virtual Column-Based Partitioning
Virtual Column是11g中的一个新功能,这种列中的数据并不实际存储于磁盘上(我们可以看成是一个类似Function的列),只有当读取的时候才实时计算。暂时不讨论性能问题,这个功能还是比较有意思的。
可以通过这样的语句来创建虚拟列。
CREATE TABLE tb_v
(col_1 number(6) not null,
col_2 number not null,
…
col_v as (col_1 *( 1+col_2));
虚拟列虽然没有实际的存储空间,但是却可以跟其他普通列一样,创建索引,作为分区键,甚至可以收集统计信息。
2、数据压缩技术
随着数据量的不断海量,CPU的不断强劲,双核四核的叫个不停,一种叫做时间换空间的优化技术应该会越来越流行。所以,数据压缩对于今后的数据库来说,应该会从核武器变成常规武器。
Oracle11g是正儿八经的要推广数据压缩技术了,专门推出了一个叫做Advance Compression的组件,全面支持普通表压缩,非结构化数据压缩(SecureFile数据压缩),Data Pump数据压缩,以及RMAN备份压缩,数据压缩技术从此名正言顺的登上历史舞台。
在Oracle9i中虽然引入了表压缩,但是有很大的限制。只能对批量装载操作(比如直接路径装载,CTAS等)涉及的数据进行压缩,普通的DML操作的数据是无法压缩的。这应该是对于写操作的压缩难题没有解决,一直遗留到Oracle11g,总算是解决了关系数据压缩的写性能问题。Oracle的表压缩是针对Block级别的数据压缩,主要技术和Oracle9i差不多,还是在Block中引入symbol表,将block中的重复数据在symbol中用一个项表示。Oracle会对block进行批量压缩,而不是每次在block中写入数据时都进行压缩,通过这种方式,可以尽量降低数据压缩对于DML操作的性能影响。这样,在block级别应该会引入一个新的参数,用于控制block中未压缩的数据量达到某个标准以后进行压缩操作。
SecureFile也是Oracle11g新推出的一项特性,用于存储非结构化数据。SecureFile也将支持数据压缩操作。这样对于传统的LOB字段也可以进行压缩,将极大的减少大型数据库的存储空间需求。当然,有得比有失,压缩和解压时,对于CPU的要求也将更高。但是,目前CPU的发展速度明显比IO和存储空间快速的情况下,压缩是大有可为的技术。通过在压缩率和压缩效率方面的不断提升,以后应该为成为各个数据库的标准配置。
除了对数据库中的数据进行压缩,Advance Compression Option还将支持备份数据的压缩。做为逻辑备份的Data Pump和物理备份的RMAN工具,都将支持该技术。在Oracle10gR2中,Data Pump已经开始支持压缩源数据,Oracle11g中则可以直接压缩导出文件,这样导出的时候就可以极大的减少存储空间的需求。在以前版本中,利用WinRAR等,经常可以将几个G的导出文件压缩到几十M,Oracle11g的白皮书上说压缩率可以达到74.67%。同样的,Oracle也在10g中开始引入RMAN的压缩技术。但是Oracle11g号称采用了更先进的ZLIB要所算法,可以比Oracle10g的压缩算法快上40%,空间需求也将减少20%。
除了上述的数据压缩技术,Oracle 11g Advanced Compression Option还将引入另外一种压缩技术。我们知道在Data Guard中,需要将日志从主库传递到备库。如果主库的事务很多,则单位时间内需要传递的日志量将相当可观。如果能将这些日志压缩后在传递,然后在备库解压后应用,将极大的减少对于网络带宽的需求,从而已减少主备库的时间差。
另外,Oracle的bitmap一直就是压缩存储的,10g中的bitmap对于9i就有比较大的改动,通过一些细节的完善,提供更好的性能和更高的稳定性,也是oracle一贯的风格。对于bitmap在Oracle11g中将如何实现,也将是非常值得关注的一个特点。
从Oracle11g开始,将没有什么是不可压缩的。使用更强大的CPU,就可以降低或者延缓对存储空间无休止的渴求,或许很多大型OLTP和大多数的数据仓库,都将从数据压缩技术中收益。
3、统计信息收集
下面围绕统计信息收集,分别对收集统计信息时可以设置的选项、对合并列收集统计信息,对表达式和函数收集统计信息以及延迟发布统计信息这四个方面做了阐述。
3.1. 设置收集统计信息时的选项
我们知道,数据库里的对象的统计信息(statistics)对于优化器得到正确的执行计划来说起着至关重要的作用。因此从10g R1开始,只要使用DBCA安装的数据库,都会自动创建一个job,该job缺省周一到周五每天晚上10点到第二天早上6点(周末则为全天)负责收集数据库所有对象的统计信息。不过,可能存在某些情况,你需要用自己的脚本来收集某些特殊对象的统计信息。但是由于你采用了自动收集统计信息,oracle就会对所有对象使用相同的选项来收集统计信息,这样你就失去了对某个对象的控制权。当你发现缺省的统计信息收集方式对某个对象不是很合适时,你必须锁定该对象的统计信息,并使用一个特殊的选项值对该对象来收集统计信息。
比如,某个表的列的数据倾斜(列为某种值的记录行数非常多,而某种值的记录行数又非常少)的非常严重,这时如果采用标准的采样率:
ESTIMATE_PERCCENT=AUTO_SAMPLE_SIZE可能就不适合了。这时你就需要单独指定该对象的采样率。我们知道,在11g之前的收集统计信息方面,oracle提供的类似的其他选项还包括:CASCADE、DEGREE、METHOD_OPT、NO_INVALIDATE、GRANULARITY。
到了11g里,则提供了更大的灵活性,从而使得你可以很简单的处理上面所说的这种情况。在11g里,上面说的这些选项可以在不同的级别上分别设置,级别由高到低分别为:global级别、数据库级别、schema级别、表级别。其中,低级别的选项覆盖高级别的选项。
比如,对于上面所举的例子来说,如果要对你的一个特殊的、列上的值倾斜的很严重的表收集统计信息时,你只需要简单的调用如下的存储过程来设置该表级别上的的ESTIMATE_PERCCENT=100即可,如下所示:
SQL> exec dbms_stats.set_table_prefs('Schema_name','Table_name',
'ESTIMATE_PERCCENT','100');
这样设置以后,当数据库在自动收集统计信息时,对于其他没有单独设置采样率的表来说,采样率会采用AUTO_SAMPLE_SIZE,而对于你单独设置的Table_name表,则会使用100的采样率来收集统计信息。
类似的,如果需要设置global级别上的选项,则调用dbms_stats.set_global_prefs;如果要设置数据库级别上的选项,则调用dbms_stats.set_database_prefs;如果要设置schema级别上的选项,则调用dbms_stats.set_schema_prefs即可。
同时到了11g里,除了上面提到的这些选项以外,还添加了另外三种新的选项:PUBLISH、INCREMENTAL、STALE_PERCENT。其中:
1) PUBLISH:收集完统计信息以后是否立即将统计信息发布到数据字典里,还是将它们存放在私有区域里。TRUE表示立即发布,FALSE表示存放到私有区域里。
2) STALE_PERCENT:确定某个对象的统计信息过时的上限,如果过时就需要重新收集统计信息,缺省为10。计算某个表的统计信息是否过时,oracle会计算自从上一次收集该表的统计信息以来,该表中被修改的数据行数占该表的总行数的百分比。然后用得出的百分比值与该选项配置的值(如果缺省,就是10)进行比较,大于10,则说明该表的统计信息过时了,需要重新收集统计信息;否则就认为该表的统计信息不过时,不用再次收集。
3) INCREMENTAL:在分区表上收集global的统计信息时(将GRANULARITY设置为GLOBAL),采用增量方式完成。使用该选项是因为对于某些分区表来说,比如按照月份进行范围分区的分区表来说,除了代表当前月的分区里的数据会经常变化以外,其他分区里的数据不会变动。因此在收集该分区表上的global的统计信息时,就没有必要再次扫描那些非当前月的分区了。如果你将INCREMENTAL设置为TRUE时,则在收集统计信息时,就不会扫描那些非当前月的分区里的数据,而只会扫描当前月的分区里的数据。最后将非当前月的分区上已经存在的统计信息加上当前月新算出来的统计信息合并就得出了分区表的global的统计信息。
可以从视图:DBA_TAB_STAT_PREFS里看到所有的收集统计信息时的各个选项的值。
3.2. 对合并列收集统计信息
对于where条件里具有两个列以上的情况,比如where c1=’A’ and c2=’B’来说,11g以前优化器评估其selectivity时,总是将每个列的selectivity相乘,从而得到整个where条件的selectiviey。但是如果两个列具有很强的依赖关系,比如汽车制造商与汽车型号这两个列来说,我们知道每个汽车制造商所生产的汽车型号几乎都不会重复,也就是说当你发出where 汽车制造商列=’XXX’ and 汽车型号列=’XXX’时,与发出where汽车型号列=’XXX’时返回的记录行数可能几乎一样。这时如果在计算where条件的selectivity时仍然采用将汽车制造商列的selectivity乘以汽车型号列的selectivity时,就会导致总的selectivity过低,从而导致优化器估计返回的记录行数过少,从而可能导致不正确的执行计划。
为了弥补这样的问题,11g以后可以让你将多个依赖程度很高列合并成一个组,然后对该组收集统计信息。具体如何实现,则可以看下面的例子。
select dbms_stats.create_extended_stats('Schema_name','Table_name','(C1,C2)') from dual;
通过调用函数dbms_stats.create_extended_stats将两个或多个列合并,并返回一个虚拟的隐藏列的列名,其名字类似于:SYS_STUW_5RHLX443AN1ZCLPE_GLE4。
然后,我们可以开始对表收集统计信息,收集完以后,你可以使用ALL|DBA|USER_STAT_EXTIONSIONS视图来查看列组合的统计信息。
exec dbms_stats.gather_table_stats('Schema_name','Table_name');
如果你要对组合列收集直方图,则可以如下所示:
exec dbms_stats.gather_table_stats('Schema_name','Table_name',
method_opt=>'for columns (C1,C2) size AUTO');
3.3 对函数以及表达式收集统计信息
如果where条件类似于function_name(table_name.column_name)=’XXX’时,则优化器在估计这样的where条件的selectivity时,总是会假设其selectivity为1%,也就是该where条件将返回table_name里总记录行数的1%的记录行数。很明显的,这种假设肯定是错误的,从而可能导致优化器产生了不够优化的执行计划。
从11g开始,我们可以对函数或者表达式收集统计信息了。该特性依赖于虚拟列,也就是说你需要先用dbms_stats.create_extended_stats函数为
function_name(table_name.column_name)创建一个虚拟列,然后对该虚拟列收集统计信息。比如下面的例子。
select dbms_stats.create_extended_stats('Schema_name','Table_name','length(C1)') from dual;
下面则显示的是对表达式来收集统计信息。
select dbms_stats.create_extended_stats('Schema_name','Table_name','C1*C2') from dual;
然后你可以对表收集统计信息时,就会为函数length(C1)对应的虚拟列收集统计信息了。如果你要对该虚拟列收集直方图,则可以如下所示:
exec dbms_stats.gather_table_stats('Schema_name','Table_name',
method_opt=>'for columns (length(C1)) size AUTO');
3.4 _PRIVATE_STATS里看到这些私有的统计信息。
为了测试这些私有统计信息,你可以有两种方法:
1) 第一种方式使用DBMS_STAT.EXPORT_PRIVATE_STATS存储过程将私有统计信息转移到你自己的统计信息表(可以使用存储过程DBMS_STATS.CREATE_STAT_TABLE来创建你自己的统计信息表)里。然后可以使用expdp导出你的统计信息表,然后再使用impdp将导出文件导入到测试环境中,再使用DBMS_STAT.IMPORT_TABLE_STATS将其导入到测试环境中进行测试。
2) 第二种方式不导出私有的统计信息,而是直接在产品库的session级别,将11g引入的新的初始化参数: OPTIMIZER_PRIVATE_STATISTICS设置为TRUE(缺省情况下该参数为FALSE)。这时你执行SQL时,优化器就会参考私有统计信息来解析SQL语句并生成执行计划了。
最后,测试完毕,发现最新的统计信息没有问题的话,你就可以使用DBMS_STAT.PUBLISH_PRIVATE_STATS在产品库上将私用统计信息发布出去,从而让优化器能够看到它们。
下面列举一个例子来简单说明这个过程。
首先设置表级别的publish选项为false:
exec dbms_stats.set_table_prefs('Schema_name','Table_name','PUBLISH','false');
然后,收集表的统计信息:
exec dbms_stats.gather_table_stats('Schema_name','Table_name');
第三,设置相关初始化参数:
alter session set optimizer_use_private_statistics = true;
第四,进行测试,运行相关的SQL语句,并检查产生的执行计划。
最后,把该表的统计信息发布出去:
exec dbms_stats.publish_private_stats('Schema_name','Table_name');
4、执行计划管理
4.1. 执行计划管理的工作原理
我们知道,SQL语句的性能很大程度上依赖于SQL语句的执行计划。如果SQL语句的执行计划发生改变,则SQL的性能很有可能发生大的变化。而SQL执行计划发生变化的原因有很多种,包括优化器版本的变化、统计信息的变化、优化器相关的各种参数的变化、添加或删除了索引、添加或删除了物化视图、修改了系统设置、以及是否应用了10g引入的SQL profile等。
在11g之前,我们可以使用存储大纲(stored outline)和SQL Profile来帮助我们固定某条SQL语句的执行计划,防止由于执行计划发生变化而导致的性能下降。不过这些技术需要DBA人为的处理,比如存储大纲,需要DBA手工创建,而SQL Profile来说,则要DBA手工应用才能生效。
从11g开始,oracle引入了SQL执行计划管理(SQL Plan Management)这个新特性,从而可以让系统自动的来控制SQL语句执行计划的稳定性,进而防止由于执行计划发生变化而导致的性能下降。
通过启用该特性,某条语句如果产生了一个新的执行计划,只有在它的性能比原来的执行计划好的情况下,才会被使用。为了实现执行计划管理,优化器会为所有执行次数超过一次的SQL语句维护该SQL语句的每个执行计划的历史列表(plan history)。优化器通过维护一个语句执行的日志条目(statement log)来识别该SQL语句是否为第二次执行。一旦优化器认出某条SQL语句为第二次执行,则优化器将该语句所生成的所有不同的执行计划插入到plan history的相关表里,而plan history里包含了优化器能够用于重新生成执行计划的所有信息,这些信息包括SQL文本、存储大纲、绑定变量以及解析环境(比如optimizer_mode之类影响优化器解析SQL语句的参数)等。
当然,11g也支持手工维护SQL语句的plan history,作为对自动维护plan history的功能补充。但是并不是说plan history里任何的执行计划都可以拿来使用。这里需要先介绍一下所谓的执行计划基准线(plan baseline)这个概念。plan baseline是plan history的一个子集,plan baseline里面的执行计划是用来比较性能好坏的一个依据。也就是说,凭什么来判断是否可以使用一个新产生的执行计划呢?就是把该新的执行计划与plan baseline里的计划进行比较来判断。某个SQL语句的执行计划可以属于plan history,但是不一定属于plan baseline。
注意:每个SQL语句都会有它自己的执行计划历史以及执行计划基准线。
那么某个SQL语句的执行计划是如何进入执行计划历史,乃至执行计划基准线的呢?
有两种方法可以将SQL语句的执行计划纳入到执行计划管理体系中去:
1) 将初始化参数:OPTIMIZER_CAPTURE_PLAN_BASELINES设置为true,则会自动的捕获SQL的执行计划。该参数缺省为false。设置为true以后,当某条SQL语句第一次执行时,该SQL语句的plan history是空的,显然该SQL语句的plan baseline也是空的。那么当该SQL第二次再次执行时,优化器会自动将该SQL语句以及相关的执行计划放入plan history,同时也会放入到plan baseline里。这就构成了最初的plan baseline,也就是说最初进入plan history的执行计划会直接自动进入plan baseline里。那么当你做了某些修改,比如添加了一个索引等,然后第三次执行该同样的SQL语句,则会产生另外一个不同的执行计划,这时新的执行计划会自动进入plan history,但是不会自动进入plan baseline。是否使用该新的执行计划,则要把该新的执行计划与plan baseline里现存的第二次执行SQL时的执行计划进行比较,如果新的执行计划成本更低,则会使用新的执行计划,否则使用plan baseline里的执行计划。
2) 使用dbms_spm包手工处理,这可以让你手工管理SQL plan baseline。使用该包,你可以直接将SQL的执行计划从shared pool里加载到plan baseline里,也可以使用dbms_spm包将已经存在的SQL Tuning Set加载到plan baseline里。同时,dbms_spm可以让你把plan history里的执行计划加入到plan baseline里。反之,你也可以使用该包将plan baseline里的执行计划移出去。
注意,某条SQL语句的plan baseline里的第一个执行计划可以像上面说的通过设置初始化参数来自动加入,但是如果你希望在plan baseline里添加该SQL语句的其他新的执行计划时,则必须使用dbms_spm包手动完成。
那么plan baseline里的执行计划是如何被使用的呢?
Oracle提供了一个初始化参数:OPTIMIZER_USE_PLAN_BASELINES,该参数缺省为true,表示要求优化器考虑使用plan baseline里的执行计划,如果设置为false,则不使用执行计划管理的特性,而又回到了11g之前的状况。
以下描述基于的前提是plan baseline里已经存在了一个SQL的执行计划。
每次优化器解析SQL语句的时候,首先仍然使用11g之前的传统方式产生一个成本最低的执行计划,然后看初始化参数:OPTIMIZER_USE_PLAN_BASELINES是否设置为true,如果为false,则直接返回所生成的执行计划。否则如果为ture,则去plan history里找是否存在相同的执行计划,如果找到了,则再去plan baseline里找是否存在相同的执行计划,如果也找到了,则直接返回该执行计划。如果在plan history里没找到相同的执行计划,则将产生的执行计划加入到plan history里,然后将执行计划与plan baseline里已经存在的执行计划进行比较,看哪个执行计划的成本低就取哪个执行计划。
如果你为某个SQL语句保存了存储大纲,则为了向下兼容,该语句会使用存储大纲。另外,即使你通过设置初始化参数:OPTIMIZER_CAPTURE_PLAN_BASELINES为true来启动自动捕获执行计划到plan baseline里,对于使用存储大纲所生成的执行计划也不会放入plan baseline里。
SQL语句的plan history以及plan baseline所涉及到的表是存放在SQL Management Base (SMB)里的,SMB里同样也存放了SQL Profiles。而SMB是数据字典的一部分,存放在SYSAUX表空间里。SMB所占用的空间会自动定期的删除。
4.2. 执行计划管理的测试
Oracle 11g提供了一个视图:dba_sql_plan_baselines,可以在该视图里查询某条SQL语句相关的plan history以及plan baseline。我们来看下面的例子。
首先创建一个测试表。
SQL> create table t1(skew number, padding varchar2(100));
SQL> insert into t1 select rownum,object_name from dba_objects;
SQL> commit;
SQL> set autotrace traceonly exp stat;
SQL> select * from t1 where skew=200;
SQL> select * from t1 where skew=200;
尽管执行两次,但是这时去查询dba_sql_plan_baselines,试图找到SQL文本为select * from t1 where skew=200的记录时,会发现没有记录,因为optimizer_capture_sql_plan_baselines缺省为false。我们将该参数设置为true以后继续测试。
SQL> alter session set optimizer_capture_sql_plan_baselines=true;
SQL> select * from t1 where skew=200; --全表扫描
SQL> select * from t1 where skew=200; --全表扫描
SQL> select signature,sql_handle,plan_name,origin,enabled,accepted, autopurge
from dba_sql_plan_baselines where sql_text like 'select * from t1 where skew=200';
SIGNATURE SQL_HANDLE PLAN_NAME ORIGIN ENA ACC AUT
---------- ------------------------ ----------------------------- ----------- --- --- ---
1.2376E+19 SYS_SQL_abc0a2c042fa089c SYS_SQL_PLAN_42fa089c844cb98a AUTO-CAPTURE YES YES YES
我们可以看到,文本为“select * from t1 where skew=200”的SQL语句在plan history里产生了一个执行计划。其中,
sql_handle表示SQL语句的句柄;
plan_name则表示该SQL的执行计划的名字;
origin表示该执行计划是如何进入plan history的,该列值为AUTO-CAPTURE则说明是由优化器自动加入的,如果为MANUAL则说明是由DBA手工加入的;
enabled表示是否被启用了,YES表示启用,NO表示禁用。如果某个执行计划为禁用,则优化器根本就不会考虑使用该执行计划;
accepted表示是否接受,也就是是否进入了plan baseline。我们看到这里的accepted为YES,说明该SQL的执行计划进入了plan baseline里;
autopurge表示该执行计划是否为定期自动删除,YES表示是,NO表示否。
我们继续测试,在skew上添加一个索引,从而让原来的SQL不走全表扫描,而改走索引扫描。
SQL> create index idx_t1 on t1(skew);
SQL> exec dbms_stats.gather_table_stats(user,'t1',cascade=>true);
SQL> select * from t1 where skew=200; --索引扫描
SQL> select signature,sql_handle,plan_name,origin,enabled,accepted,fixed,autopurge
from dba_sql_plan_baselines where sql_text like 'select * from t1 where skew=200';
SIGNATURE SQL_HANDLE PLAN_NAME ORIGIN ENA ACC AUT
---------- ------------------------ ----------------------------- ----------- --- --- ---
1.2376E+19 SYS_SQL_abc0a2c042fa089c SYS_SQL_PLAN_42fa089c844cb98a AUTO-CAPTURE YES YES YES
1.2376E+19 SYS_SQL_abc0a2c042fa089c SYS_SQL_PLAN_42fa089cdbd90e8e AUTO-CAPTURE YES NO YES
这时我们可以看到,dba_sql_plan_baselines视图里多了一个执行计划,也就是我们后面那个使用了索引扫描的执行计划。而该执行计划的accepted为NO,说明该计划并没有进入plan baseline里,但是进入了plan history里。
这时,我们可以通过调用dbms_spm包来手工将走索引的执行计划加入到plan baseline里。如下所示,将accepted改为YES。
SQL> dbms_spm.alter_sql_plan_baseline(
sql_handle => 'SYS_SQL_abc0a2c042fa089c',
plan_name => 'SYS_SQL_PLAN_42fa089c844cb98a',
attribute_name => 'ACCEPTED',
attribute_value => 'YES');
然后再次查询dba_sql_plan_baselines视图,可以发现后面的执行计划的accepted变为了YES。
SQL> select signature,sql_handle,plan_name,origin,enabled,accepted,fixed,autopurge
from dba_sql_plan_baselines where sql_text like 'select * from t1 where skew=200';
SIGNATURE SQL_HANDLE PLAN_NAME ORIGIN ENA ACC AUT
---------- ------------------------ ----------------------------- ----------- --- --- ---
1.2376E+19 SYS_SQL_abc0a2c042fa089c SYS_SQL_PLAN_42fa089c844cb98a AUTO-CAPTURE YES YES YES
1.2376E+19 SYS_SQL_abc0a2c042fa089c SYS_SQL_PLAN_42fa089cdbd90e8e AUTO-CAPTURE YES YES YES
如果我们要手工删除plan baseline里的执行计划,则可以调用dbms_spm里的存储过程来实现。
SQL> var cnt number;
SQL> exec :cnt :=
dbms_spm.purge_sql_plan_baseline('SYS_SQL_abc0a2c042fa089c');
删除指定SQL语句的执行计划以后,再去查询dba_sql_plan_baselines就会发现上面测试SQL语句的执行计划不存在了。
从上面的描述可以看出,SQL Plan Management特性的主要作用就是通过引入一个SQL plan baseline,从而保证SQL语句执行计划的稳定,进而保证系统性能的稳定。本质上,它属于11g之前的存储大纲的升级版,而且是自动实现的,不需要人工干预。
5、自动内存管理
Auto Memory Management是Oracle10g提出来的一个新特性,在最新的Oracle11g数据库中又得到了进一步的发展。
通过使用自动内存管理,Oracle数据库中的PGA和SGA内存之间可以互相转换,根据当前的工作负载来自动设定Oracle内存区域中的PGA和SGA的大小。这种间接的内存转换依赖于操作系统的共享内存的释放机制来获得内部实例的调优。目前这种技术可以应用于Linux, Solaris, HPUX, AIX 和Windows等操作系统上。
首先我们来回顾下Oracle10g的自动内存管理特性。在Oracle10g的数据库中,只有SHARED_POOL_SIZE、DB_CACHE_SIZE、LARGE_POOL_SIZE、JAVA_POOL_SIZE、STREAMS_POOL_SIZE五个SGA组件可以被自动调整,其中PGA的最大值由初始化参数PGA_AGGREGATE_TARGET决定,SGA的最大值由初始化参数SGA_TARGET决定。
在Oracle11g数据库中,使用自动内存管理特性不再需要设定参数PGA_AGGREGATE_TARGET和SGA_TARGET,因为这两个参数都已经被修改成自动调优的,除非想指定PGA和SGA的最小值才需要设定这两个参数。
在Oracle11g数据库中,则需要设置一个叫做MEMORY_TARGET的初始化参数,这个参数是指整个Oracle实例所能使用的内存大小,包括PGA和SGA的整体大小,在MEMORY_TARGET的内存大小之内,PGA和SGA所用的内存可以根据当前负载情况自动相互转换。
如果当初始设定的MEMORY_TARGET的内存不够当前数据库使用的时候,Oracle11g还提供了另外一个初始化参数MEMORY_MAX_TARGET,当原始设定的内存不够使用的时候,可以手工来动态 调节MEMORY_TARGET的大小,但是不允许超过MEMORY_MAX_TARGET的值。
下面这张图简单明了的描述出了Oracle11g数据库内存大小的设定参数。
此外,Oracle11g数据库还提供了几个用于监控自动内存管理的视图:
V$MEMORY_DYNAMIC_COMPONENTS:描述当前所有内存组件的状态
V$MEMORY_RESIZE_OPS:循环记录最后800次的SGA大小调整请求
X$KMGSTFR:循环记录最后800次的SGA的转换地址
_MEMORY_MANAGEMENT_TRACING=23:对于所有的内存转换调整行为均记录保存为跟踪文件