关于Oracle的性能调整,一般包括两个方面,一是指Oracle数据库本身的调整,比如SGA、PGA的优化设置,二是连接Oracle的应用程序以及SQL语句的优化。做好这两个方面的优化,就可以使一套完整的Oracle应用系统处于良好的运行状态。
在下图所示的例子中,并行的全表扫描由于不需要按照排序重新读取数据块,所以耗系统资源较少,并且IO操作也不多。当然,由于SQL语句执行过程中没有排序,得到预期结果的时间就长了,而资源耗费少了。假设这个执行计划从开始到返回结果耗时 10 秒,同时产生了 5000 个 db_block_gets
Oracle提供了几个 optimizer_mode 的设置参数,使你能够得到想要的最好的执行计划。
optimizer_mode = first_rows
设置为这种CBO模式以后,SQL语句返回结果的速度会尽可能的快,而不管系统全部的查询是否会耗时较长或者耗系统资源过多。由于利用索引会使查询速度加快,所以 first_rows 优化模式会在全表扫描上进行索引扫描。这种优化模式一般适合于一些OLTP系统,满足用户能够在较短时间内看到较小查询结果集的要求。
optimizer_mode = all_rows
设置为这种CBO模式以后,将保证消耗的所有计算资源最小,尽管有时查询结束以后没有结果返回。all_rows 的优化模式更倾向于全表扫描,而不是全索引扫描和利用索引排序,因此这种优化模式适合于数据查看实时性不是那么强的数据仓库、决策支持系统和面向批处理的数据库(batch-oriented databases)等。
optimizer_mode = first_rows_n
Oracle 9i 对一些预期返回结果集的数据量小的SQL语句优化模式进行了加强,增加了四个参数值:first_rows_1、first_rows_10、 first_rows_100、first_rows_1000。CBO通过 first_rows_n 中的 n 值,决定了返回结果集数量的基数,我们可能仅仅需要查询结果集中的一部分,CBO就根据这样的 n 值来决定是否使用索引扫描。
optimizer_mode = rule
基于规则的优化器模式,RBO,是早期Oracle版本使用过的一种优化模式。由于RBO不支持自1994年Oracle版本的新特性,如 bitmap indexes,table partitions,function-based indexes等,所以在以后Oracle版本中已经不再更新RBO,并且也不推荐用户使用RBO这种优化模式了。
从上面的讨论可以看出,optimizer_mode 参数的设置对CBO是非常重要的,决定了CBO的基本模式,同时还有一些其他的参数也对CBO有着极大的影响。由于CBO的重要性,Oracle提供了一些系统级的参数来调整CBO的全局性能,这些调整参数包括索引扫描与全部扫描的选择、表连接方式的选择,等等。下面简单讨论一下。
optimizer_index_cost_adj
这个参数用于调整使用索引的访问路径的成本算法,参数值越小,索引访问的成本就越低。
optimizer_index_caching
这个参数告诉Oracle在内存缓冲区中索引的数量。该参数的设置会影响CBO如何决定使用表连接(嵌套循环)的索引还是使用全表扫描。
db_file_multiblock_read_count
这个参数的值被设置较大的时候,CBO就会认为离散的、多数据块的读取会比顺序读取的代价更低,使得CBO更倾向于全表扫描。
parallel_automatic_tuning
这个参数值被设置为 on 的时候,表示使用并行的全表扫描,由于并行的全表扫描比较快,所以CBO认为索引的访问是高成本的,同时就更倾向于全表扫描。
hash_area_size
如果不使用 pga_aggregate_target 参数的话,该参数有效。该参数的设置大小决定CBO是否更加倾向于 hash joins ,而不是嵌套循环和表连接的索引合并。
sort_area_size
如果不使用 pga_aggregate_target 参数的话,该参数有效。该参数的设置大小影响CBO决定是否进行索引访问和结果集的排序,参数值越大,在内存中排序的可能性就越大,CBO也就更加倾向于排序。
由于对这些参数值的修改会影响到系统中成千上万的SQL语句的执行计划,所以Oracle并不推荐修改这些参数的缺省值。
在对CBO的参数有了大致的了解以后,下面讨论如何根据提供给CBO的数据帮助CBO制定出一个好的执行计划。
2.4.2 CBO的统计
对于CBO来说,最重要的是定义和管理好你的统计数据,为了使CBO能够为你的SQL语句产生一个最好的执行计划,必须要有与SQL语句相关的表和索引统计数据。只有当CBO知道了相关的信息,如表的大小、分布、基数以及列值的可选性等,才能对SQL语句作出正确的判断,从而得到最好的执行计划。
下面讨论一下如何获得高质量的CBO统计数据,如何为你的数据库系统创建一个适当的CBO环境。
CBO 产生最好执行计划的能力来自于统计数据的有效性,获得统计数据的比较过时的方法是 analyze table 和 dbms_utility ,这两种方法对SQL语句的性能有一些危害,因为我们知道,CBO是使用对象统计数据(object statistics)来为所有的SQL语句选择最好的执行计划。
dbms_stats 应用功能包是产生统计数据较好的方法,特别对大型分区表而言。下面看一个使用 dbms_stats 的例子。
exec dbms_stats.gather_schema_stats(
ownname => 'SCOTT',
options => 'GATHER AUTO',
estimate_percent => dbms_stats.auto_sample_size,
method_opt => 'for all columns size repeat',
degree => 34
)
上面例子中的options参数的几个可选值需要说明一下。
GATHER 重新分析整个schema,产生统计数据;
GATHER EMPTY 仅分析那些还没有统计数据的表;
GATHER STALE 仅重新分析那些发生了10%变化的表(变化原因可能是 inserts, updates , deletes )
GATHER AUTO 仅重新分析那些还没有统计数据和发生了10%变化的表,该选项相当于 GATHER EMPTY 和 GATHER STALE 同时使用。
使用 GATHER AUTO 和 GATHER STALE 两个选项需要进行监控,如果你执行了 ALTER TABLE XXX MONITORING 命令,Oracle利用 dba_tab_modifications 视图跟踪表的变化,记录了最近一次统计数据分析以来的 insert , update , delete 的准确记录数。
SQL> desc dba_tab_modifications;
Name Type
----------------- ---------------
TABLE_OWNER VARCHAR2(30)
TABLE_NAME VARCHAR2(30)
PARTITION_NAME VARCHAR2(30)
SUBPARTITION_NAME VARCHAR2(30)
INSERTS NUMBER
UPDATES NUMBER
DELETES NUMBER
TIMESTAMP DATE
TRUNCATED VARCHAR2(3)
比较有趣的一个选项是 GATHER STALE ,比如在一个数据更新频繁的OLTP系统中,几乎所有的统计数据都会很快的过时,而我们必须记住 GATHER STALE 选项是在表中10%的记录发生变化时才对该表重新分析产生统计数据,因此除了只读表以外的所有表几乎使用 GATHER STALE 选项重新分析产生统计数据,所以 GATHER STALE 选项主要还是用于一些主要是只读表组成的系统中。
在上面使用 dbms_stats 的例子中,我们看到了一个参数 estimate_percent ,它的值是 dbms_stats.auto_sample_size, 这个参数值是 Oracle 9i 才开始使用的,这个参数值的出现极大方便了统计数据的分析产生。
我们知道,统计数据的质量越高,CBO产生最好执行计划的能力就越强,但是由于数据库统计采样大小的问题,对一个大型数据库系统做一个完整的统计数据分析产生将会耗时数天,最好的办法就是在高质量的统计数据和数据库统计采样大小之间得到一个平衡点。
在早一些的Oracle版本中,为了得到统计数据,DBA不得不猜测一个最好的数据采样大小百分比。但是从 Oracle 9i 开始,可以通过 dbms_stats 包来自己指定 estimate_percent 参数的值了,那就是 dbms_stats.auto_sample_size
通过这种方式设置了自动采样大小以后,我们可以通过下列数据字典视图的 sample_size 字段来验证这些自动产生的统计采样大小。
DBA_ALL_TABLES
DBA_INDEXES
DBA_IND_PARTITIONS
DBA_IND_SUBPARTITIONS
DBA_OBJECT_TABLES
DBA_PART_COL_STATISTICS
DBA_SUBPART_COL_STATISTICS
DBA_TABLES
DBA_TAB_COLS
DBA_TAB_COLUMNS
DBA_TAB_COL_STATISTICS
DBA_TAB_PARTITIONS
DBA_TAB_SUBPARTITIONS
使用自动统计采样以后,Oracle会根据表的大小和列值的分布在5%到20%之间取值。记住:你的统计数据质量越高,CBO作出的决定就越对你有利。
现在我们对CBO统计数据应该有一些了解了,下面来看看在一个成功的Oracle系统是如何管理CBO统计数据。
2.4.3 CBO的正确环境
成功使用CBO的关键是稳定性,下面是一些成功使用CBO的基本事项。
●只在必需的时候才进行统计数据的重新分析
Oracle DBA们最容易犯的一个普遍错误就是经常性的对系统的统计数据进行重新分析。记住:做这件事的唯一目的是改变SQL语句的执行计划,如果这个执行计划没有被破坏,就不要去修复它。如果你对SQL语句的性能还满意的话,重新分析产生统计数据以后可能会产生较大的性能问题,并给开发团队带来影响。实际运用中,也是极少数的Oracle系统才会周期性的对统计数据进行重新分析。
一般来讲,一个数据库应用系统的基本架构是不会轻易改变,大数据量的表仍然是很大,索引列的分布、基数值等等也很少变化。只有下列几种情况的数据库才可能经常对整个系统的统计数据重新分析:
1、用于数据分析的数据库
有一些由于科学试验数据分析的数据库系统,经常会更换整个一套的试验数据,那么这种情况下当数据库重新load了一套数据以后,可以立即重新对统计数据进行分析。
2、高度变化的数据库
这是极少数的例子,表的大小或者索引列的数据在剧烈的变化,比如一张表有100条记录,一周以后就变成10000条记录。这种情况下也可以考虑周期性的进行统计数据分析。
●强迫开发人员调整自己的SQL
很多开发人员错误的认为,他们的任务就是编写SQL语句然后从数据库中获得正确的数据。但是实际上编写出SQL语句只是开发人员一半的工作,在一个成功的 Oracle应用系统中,会要求开发人员的SQL语句采用最优化的方式访问数据库,并且保证SQL语句的执行计划在新的SQL之间的可移植性。
令人惊讶的是,在许多Oracle应用系统中都不怎么考虑具体SQL语句的执行计划,认为CBO是很智能的,无论如何都可以为我们提供最好的SQL语句执行计划。
同一个查询在SQL语句中可能有不同方式的写法,而每一种写法都可能有不同的执行计划。观察下面的例子,每一个查询的结果都是一样的,但是执行计划却相去甚远。
-- 使用了不正确的子查询
select
book_title
from
book
where
book_key not in (select book_key from sales);
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=64)
1 0 FILTER
2 1 TABLE ACCESS (FULL) OF 'BOOK' (Cost=1 Card=1 Bytes=64)
3 1 TABLE ACCESS (FULL) OF 'SALES' (Cost=1 Card=5 Bytes=25)
-- 使用了两张表的外连接
select
book_title
from
book b,
sales s
where
b.book_key = s.book_key(+)
and
quantity is null;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=100 Bytes=8200)
1 0 FILTER
2 1 FILTER
3 2 HASH JOIN (OUTER)
4 3 TABLE ACCESS (FULL) OF 'BOOK' (Cost=1 Card=20 Bytes=1280)
5 3 TABLE ACCESS (FULL) OF 'SALES' (Cost=1 Card=100 Bytes=1800)
-- 使用了三个正确的子查询
select
book_title
from
book
where
book_title not in (
select
distinct
book_title
from
book,
sales
where
book.book_key = sales.book_key
and
quantity > 0);
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=59)
1 0 FILTER
2 1 TABLE ACCESS (FULL) OF 'BOOK' (Cost=1 Card=1 Bytes=59)
3 1 FILTER
4 3 NESTED LOOPS (Cost=6 Card=1 Bytes=82)
5 4 TABLE ACCESS (FULL) OF 'SALES' (Cost=1 Card=5 Bytes=90)
6 4 TABLE ACCESS (BY INDEX ROWID) OF 'BOOK' (Cost=1 Card=1)
7 6 INDEX (UNIQUE SCAN) OF 'PK_BOOK' (UNIQUE)
我们看到,正确的SQL语句写法产生的执行计划是如此的不同。明智的开发人员知道如何去编写能够产生最好执行计划的SQL语句,明智的Oracle应用系统也会主动训练开发人员去编写最有效的SQL语句。
下面是一些帮助开发人员优化SQL语句的技巧:
1、 使用 autotrace 和 TKPROF 功能去分析SQL语句的执行计划;
2、 保证所有生产环境中的SQL语句都是在测试环境中经过优化的;
3、 制定一个性能优化的标准,而不是只要求开发人员编写出最快的SQL语句。根据这种标准,好的开发人员应该能够写出最有效的SQL语句。
●谨慎管理CBO统计数据
成功的Oracle系统会谨慎管理他们的CBO统计数据,以保证CBO在测试环境和生产环境中以同样的方式工作。一个聪明的DBA会在得到高质量的CBO统计数据以后,把这些统计数据移植到测试环境中,这样SQL语句的执行计划在测试环境和生产环境中就是一样的了。
对 DBA来说,一个重要的工作就是收集和发布CBO统计数据,并随时保持一套当前运行环境的最精确的统计数据。在一些情况下,可能会有不止一套的优化统计数据。比如,对OLTP运行的最好的统计数据可能对数据仓库运行却不是最好的,在这种情况下,DBA就需要保持两套统计数据,并根据不同的运行条件导入系统。
可以使用 dbms_stats 包中的 export_system_stats 存储过程来完成CBO统计数据的导出。下面的例子中,我们把当前CBO统计数据导出到一张名叫 stats_table_oltp的表中。
dbms_stats.export_system_stats(‘stats_table_oltp’)
导出以后,我们就可以把这张表拷贝到别的实例中,当系统的运行模式改变以后,使用 dbms_stats 包中的 import_system_stats 存储过程来完成CBO统计数据的导入。
dbms_stats.import_system_stats(‘stats_table_oltp’)
●千万不要随便改动CBO参数的值
改动CBO相关参数的值是非常危险的,因为一个小小的改动可能就会对整个系统的执行性能带来极大的负面影响,只有在经过严格的系统测试以后才能改动这些参数的值。可能带来极大影响的参数值包括:optimizer_mode, optimizer_index_cost_adj, and optimizer_index_caching。而其他参数,比如 hash_area_size , sort_area_size,参数值的改变就不是那么危险了,可以在会话级进行改变以帮助CBO优化查询。
●保证静态的执行计划
成功的CBO应用会通过谨慎管理统计数据来锁定SQL执行计划,同时保证存储的优化计划的稳定性,或者在具体的SQL语句中加入一些细节上的提示。
记住:重新分析一个系统的统计数据,可能会导致成千上万的SQL语句改变其执行计划。许多Oracle应用系统要求所有的SQL语句在测试环境中经过验证,保证在功能上和生产环境是一致的。
2.4.4 CBO的思考
尽管我们已经对CBO的不少细节有了了解,但是由于随着Oracle新版本的不断推出,CBO变得越来越强大,同时也越来越复杂,我们仍然有许多关于CBO的知识需要学习。
下面是一些关于CBO调整的提纲性的建议,供准备进行CBO调整的DBA们思考。
●DBA可以提供一些Oracle参数的配置对CBO进行控制,但是只能在有限的环境下谨慎的改变这些参数;
●CBO依靠统计数据来产生SQL语句的优化的执行计划,可以通过 dbms_stats 包来分析、产生统计数据;
●DBA们的一项重要任务就是收集、管理CBO统计数据,这些数据可以被收集、存储,也可以在相关的实例中进行移植,以保证执行计划的连贯性。
● 在没有使用 export_system_stats 存储过程导出原来的统计数据以前,重新对系统的统计数据进行分析是十分危险的,因为成千上万的SQL语句的执行计划将可能全部改变,而你却不能恢复原来的 SQL性能。只有在系统的数据发生巨大变化时,才可能需要对整个系统的统计数据进行重新分析
在下图所示的例子中,并行的全表扫描由于不需要按照排序重新读取数据块,所以耗系统资源较少,并且IO操作也不多。当然,由于SQL语句执行过程中没有排序,得到预期结果的时间就长了,而资源耗费少了。假设这个执行计划从开始到返回结果耗时 10 秒,同时产生了 5000 个 db_block_gets
Oracle提供了几个 optimizer_mode 的设置参数,使你能够得到想要的最好的执行计划。
optimizer_mode = first_rows
设置为这种CBO模式以后,SQL语句返回结果的速度会尽可能的快,而不管系统全部的查询是否会耗时较长或者耗系统资源过多。由于利用索引会使查询速度加快,所以 first_rows 优化模式会在全表扫描上进行索引扫描。这种优化模式一般适合于一些OLTP系统,满足用户能够在较短时间内看到较小查询结果集的要求。
optimizer_mode = all_rows
设置为这种CBO模式以后,将保证消耗的所有计算资源最小,尽管有时查询结束以后没有结果返回。all_rows 的优化模式更倾向于全表扫描,而不是全索引扫描和利用索引排序,因此这种优化模式适合于数据查看实时性不是那么强的数据仓库、决策支持系统和面向批处理的数据库(batch-oriented databases)等。
optimizer_mode = first_rows_n
Oracle 9i 对一些预期返回结果集的数据量小的SQL语句优化模式进行了加强,增加了四个参数值:first_rows_1、first_rows_10、 first_rows_100、first_rows_1000。CBO通过 first_rows_n 中的 n 值,决定了返回结果集数量的基数,我们可能仅仅需要查询结果集中的一部分,CBO就根据这样的 n 值来决定是否使用索引扫描。
optimizer_mode = rule
基于规则的优化器模式,RBO,是早期Oracle版本使用过的一种优化模式。由于RBO不支持自1994年Oracle版本的新特性,如 bitmap indexes,table partitions,function-based indexes等,所以在以后Oracle版本中已经不再更新RBO,并且也不推荐用户使用RBO这种优化模式了。
从上面的讨论可以看出,optimizer_mode 参数的设置对CBO是非常重要的,决定了CBO的基本模式,同时还有一些其他的参数也对CBO有着极大的影响。由于CBO的重要性,Oracle提供了一些系统级的参数来调整CBO的全局性能,这些调整参数包括索引扫描与全部扫描的选择、表连接方式的选择,等等。下面简单讨论一下。
optimizer_index_cost_adj
这个参数用于调整使用索引的访问路径的成本算法,参数值越小,索引访问的成本就越低。
optimizer_index_caching
这个参数告诉Oracle在内存缓冲区中索引的数量。该参数的设置会影响CBO如何决定使用表连接(嵌套循环)的索引还是使用全表扫描。
db_file_multiblock_read_count
这个参数的值被设置较大的时候,CBO就会认为离散的、多数据块的读取会比顺序读取的代价更低,使得CBO更倾向于全表扫描。
parallel_automatic_tuning
这个参数值被设置为 on 的时候,表示使用并行的全表扫描,由于并行的全表扫描比较快,所以CBO认为索引的访问是高成本的,同时就更倾向于全表扫描。
hash_area_size
如果不使用 pga_aggregate_target 参数的话,该参数有效。该参数的设置大小决定CBO是否更加倾向于 hash joins ,而不是嵌套循环和表连接的索引合并。
sort_area_size
如果不使用 pga_aggregate_target 参数的话,该参数有效。该参数的设置大小影响CBO决定是否进行索引访问和结果集的排序,参数值越大,在内存中排序的可能性就越大,CBO也就更加倾向于排序。
由于对这些参数值的修改会影响到系统中成千上万的SQL语句的执行计划,所以Oracle并不推荐修改这些参数的缺省值。
在对CBO的参数有了大致的了解以后,下面讨论如何根据提供给CBO的数据帮助CBO制定出一个好的执行计划。
2.4.2 CBO的统计
对于CBO来说,最重要的是定义和管理好你的统计数据,为了使CBO能够为你的SQL语句产生一个最好的执行计划,必须要有与SQL语句相关的表和索引统计数据。只有当CBO知道了相关的信息,如表的大小、分布、基数以及列值的可选性等,才能对SQL语句作出正确的判断,从而得到最好的执行计划。
下面讨论一下如何获得高质量的CBO统计数据,如何为你的数据库系统创建一个适当的CBO环境。
CBO 产生最好执行计划的能力来自于统计数据的有效性,获得统计数据的比较过时的方法是 analyze table 和 dbms_utility ,这两种方法对SQL语句的性能有一些危害,因为我们知道,CBO是使用对象统计数据(object statistics)来为所有的SQL语句选择最好的执行计划。
dbms_stats 应用功能包是产生统计数据较好的方法,特别对大型分区表而言。下面看一个使用 dbms_stats 的例子。
exec dbms_stats.gather_schema_stats(
ownname => 'SCOTT',
options => 'GATHER AUTO',
estimate_percent => dbms_stats.auto_sample_size,
method_opt => 'for all columns size repeat',
degree => 34
)
上面例子中的options参数的几个可选值需要说明一下。
GATHER 重新分析整个schema,产生统计数据;
GATHER EMPTY 仅分析那些还没有统计数据的表;
GATHER STALE 仅重新分析那些发生了10%变化的表(变化原因可能是 inserts, updates , deletes )
GATHER AUTO 仅重新分析那些还没有统计数据和发生了10%变化的表,该选项相当于 GATHER EMPTY 和 GATHER STALE 同时使用。
使用 GATHER AUTO 和 GATHER STALE 两个选项需要进行监控,如果你执行了 ALTER TABLE XXX MONITORING 命令,Oracle利用 dba_tab_modifications 视图跟踪表的变化,记录了最近一次统计数据分析以来的 insert , update , delete 的准确记录数。
SQL> desc dba_tab_modifications;
Name Type
----------------- ---------------
TABLE_OWNER VARCHAR2(30)
TABLE_NAME VARCHAR2(30)
PARTITION_NAME VARCHAR2(30)
SUBPARTITION_NAME VARCHAR2(30)
INSERTS NUMBER
UPDATES NUMBER
DELETES NUMBER
TIMESTAMP DATE
TRUNCATED VARCHAR2(3)
比较有趣的一个选项是 GATHER STALE ,比如在一个数据更新频繁的OLTP系统中,几乎所有的统计数据都会很快的过时,而我们必须记住 GATHER STALE 选项是在表中10%的记录发生变化时才对该表重新分析产生统计数据,因此除了只读表以外的所有表几乎使用 GATHER STALE 选项重新分析产生统计数据,所以 GATHER STALE 选项主要还是用于一些主要是只读表组成的系统中。
在上面使用 dbms_stats 的例子中,我们看到了一个参数 estimate_percent ,它的值是 dbms_stats.auto_sample_size, 这个参数值是 Oracle 9i 才开始使用的,这个参数值的出现极大方便了统计数据的分析产生。
我们知道,统计数据的质量越高,CBO产生最好执行计划的能力就越强,但是由于数据库统计采样大小的问题,对一个大型数据库系统做一个完整的统计数据分析产生将会耗时数天,最好的办法就是在高质量的统计数据和数据库统计采样大小之间得到一个平衡点。
在早一些的Oracle版本中,为了得到统计数据,DBA不得不猜测一个最好的数据采样大小百分比。但是从 Oracle 9i 开始,可以通过 dbms_stats 包来自己指定 estimate_percent 参数的值了,那就是 dbms_stats.auto_sample_size
通过这种方式设置了自动采样大小以后,我们可以通过下列数据字典视图的 sample_size 字段来验证这些自动产生的统计采样大小。
DBA_ALL_TABLES
DBA_INDEXES
DBA_IND_PARTITIONS
DBA_IND_SUBPARTITIONS
DBA_OBJECT_TABLES
DBA_PART_COL_STATISTICS
DBA_SUBPART_COL_STATISTICS
DBA_TABLES
DBA_TAB_COLS
DBA_TAB_COLUMNS
DBA_TAB_COL_STATISTICS
DBA_TAB_PARTITIONS
DBA_TAB_SUBPARTITIONS
使用自动统计采样以后,Oracle会根据表的大小和列值的分布在5%到20%之间取值。记住:你的统计数据质量越高,CBO作出的决定就越对你有利。
现在我们对CBO统计数据应该有一些了解了,下面来看看在一个成功的Oracle系统是如何管理CBO统计数据。
2.4.3 CBO的正确环境
成功使用CBO的关键是稳定性,下面是一些成功使用CBO的基本事项。
●只在必需的时候才进行统计数据的重新分析
Oracle DBA们最容易犯的一个普遍错误就是经常性的对系统的统计数据进行重新分析。记住:做这件事的唯一目的是改变SQL语句的执行计划,如果这个执行计划没有被破坏,就不要去修复它。如果你对SQL语句的性能还满意的话,重新分析产生统计数据以后可能会产生较大的性能问题,并给开发团队带来影响。实际运用中,也是极少数的Oracle系统才会周期性的对统计数据进行重新分析。
一般来讲,一个数据库应用系统的基本架构是不会轻易改变,大数据量的表仍然是很大,索引列的分布、基数值等等也很少变化。只有下列几种情况的数据库才可能经常对整个系统的统计数据重新分析:
1、用于数据分析的数据库
有一些由于科学试验数据分析的数据库系统,经常会更换整个一套的试验数据,那么这种情况下当数据库重新load了一套数据以后,可以立即重新对统计数据进行分析。
2、高度变化的数据库
这是极少数的例子,表的大小或者索引列的数据在剧烈的变化,比如一张表有100条记录,一周以后就变成10000条记录。这种情况下也可以考虑周期性的进行统计数据分析。
●强迫开发人员调整自己的SQL
很多开发人员错误的认为,他们的任务就是编写SQL语句然后从数据库中获得正确的数据。但是实际上编写出SQL语句只是开发人员一半的工作,在一个成功的 Oracle应用系统中,会要求开发人员的SQL语句采用最优化的方式访问数据库,并且保证SQL语句的执行计划在新的SQL之间的可移植性。
令人惊讶的是,在许多Oracle应用系统中都不怎么考虑具体SQL语句的执行计划,认为CBO是很智能的,无论如何都可以为我们提供最好的SQL语句执行计划。
同一个查询在SQL语句中可能有不同方式的写法,而每一种写法都可能有不同的执行计划。观察下面的例子,每一个查询的结果都是一样的,但是执行计划却相去甚远。
-- 使用了不正确的子查询
select
book_title
from
book
where
book_key not in (select book_key from sales);
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=64)
1 0 FILTER
2 1 TABLE ACCESS (FULL) OF 'BOOK' (Cost=1 Card=1 Bytes=64)
3 1 TABLE ACCESS (FULL) OF 'SALES' (Cost=1 Card=5 Bytes=25)
-- 使用了两张表的外连接
select
book_title
from
book b,
sales s
where
b.book_key = s.book_key(+)
and
quantity is null;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=100 Bytes=8200)
1 0 FILTER
2 1 FILTER
3 2 HASH JOIN (OUTER)
4 3 TABLE ACCESS (FULL) OF 'BOOK' (Cost=1 Card=20 Bytes=1280)
5 3 TABLE ACCESS (FULL) OF 'SALES' (Cost=1 Card=100 Bytes=1800)
-- 使用了三个正确的子查询
select
book_title
from
book
where
book_title not in (
select
distinct
book_title
from
book,
sales
where
book.book_key = sales.book_key
and
quantity > 0);
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=59)
1 0 FILTER
2 1 TABLE ACCESS (FULL) OF 'BOOK' (Cost=1 Card=1 Bytes=59)
3 1 FILTER
4 3 NESTED LOOPS (Cost=6 Card=1 Bytes=82)
5 4 TABLE ACCESS (FULL) OF 'SALES' (Cost=1 Card=5 Bytes=90)
6 4 TABLE ACCESS (BY INDEX ROWID) OF 'BOOK' (Cost=1 Card=1)
7 6 INDEX (UNIQUE SCAN) OF 'PK_BOOK' (UNIQUE)
我们看到,正确的SQL语句写法产生的执行计划是如此的不同。明智的开发人员知道如何去编写能够产生最好执行计划的SQL语句,明智的Oracle应用系统也会主动训练开发人员去编写最有效的SQL语句。
下面是一些帮助开发人员优化SQL语句的技巧:
1、 使用 autotrace 和 TKPROF 功能去分析SQL语句的执行计划;
2、 保证所有生产环境中的SQL语句都是在测试环境中经过优化的;
3、 制定一个性能优化的标准,而不是只要求开发人员编写出最快的SQL语句。根据这种标准,好的开发人员应该能够写出最有效的SQL语句。
●谨慎管理CBO统计数据
成功的Oracle系统会谨慎管理他们的CBO统计数据,以保证CBO在测试环境和生产环境中以同样的方式工作。一个聪明的DBA会在得到高质量的CBO统计数据以后,把这些统计数据移植到测试环境中,这样SQL语句的执行计划在测试环境和生产环境中就是一样的了。
对 DBA来说,一个重要的工作就是收集和发布CBO统计数据,并随时保持一套当前运行环境的最精确的统计数据。在一些情况下,可能会有不止一套的优化统计数据。比如,对OLTP运行的最好的统计数据可能对数据仓库运行却不是最好的,在这种情况下,DBA就需要保持两套统计数据,并根据不同的运行条件导入系统。
可以使用 dbms_stats 包中的 export_system_stats 存储过程来完成CBO统计数据的导出。下面的例子中,我们把当前CBO统计数据导出到一张名叫 stats_table_oltp的表中。
dbms_stats.export_system_stats(‘stats_table_oltp’)
导出以后,我们就可以把这张表拷贝到别的实例中,当系统的运行模式改变以后,使用 dbms_stats 包中的 import_system_stats 存储过程来完成CBO统计数据的导入。
dbms_stats.import_system_stats(‘stats_table_oltp’)
●千万不要随便改动CBO参数的值
改动CBO相关参数的值是非常危险的,因为一个小小的改动可能就会对整个系统的执行性能带来极大的负面影响,只有在经过严格的系统测试以后才能改动这些参数的值。可能带来极大影响的参数值包括:optimizer_mode, optimizer_index_cost_adj, and optimizer_index_caching。而其他参数,比如 hash_area_size , sort_area_size,参数值的改变就不是那么危险了,可以在会话级进行改变以帮助CBO优化查询。
●保证静态的执行计划
成功的CBO应用会通过谨慎管理统计数据来锁定SQL执行计划,同时保证存储的优化计划的稳定性,或者在具体的SQL语句中加入一些细节上的提示。
记住:重新分析一个系统的统计数据,可能会导致成千上万的SQL语句改变其执行计划。许多Oracle应用系统要求所有的SQL语句在测试环境中经过验证,保证在功能上和生产环境是一致的。
2.4.4 CBO的思考
尽管我们已经对CBO的不少细节有了了解,但是由于随着Oracle新版本的不断推出,CBO变得越来越强大,同时也越来越复杂,我们仍然有许多关于CBO的知识需要学习。
下面是一些关于CBO调整的提纲性的建议,供准备进行CBO调整的DBA们思考。
●DBA可以提供一些Oracle参数的配置对CBO进行控制,但是只能在有限的环境下谨慎的改变这些参数;
●CBO依靠统计数据来产生SQL语句的优化的执行计划,可以通过 dbms_stats 包来分析、产生统计数据;
●DBA们的一项重要任务就是收集、管理CBO统计数据,这些数据可以被收集、存储,也可以在相关的实例中进行移植,以保证执行计划的连贯性。
● 在没有使用 export_system_stats 存储过程导出原来的统计数据以前,重新对系统的统计数据进行分析是十分危险的,因为成千上万的SQL语句的执行计划将可能全部改变,而你却不能恢复原来的 SQL性能。只有在系统的数据发生巨大变化时,才可能需要对整个系统的统计数据进行重新分析