数据库优化的讨论可以说是一个永恒的主题。资深的Oracle优化人员通常会要求提出性能问题的人对数据库做一个statspack,贴出数据库配置等等。还有的人认为要抓出执行最慢的语句来进行优化。但实际情况是,提出疑问的人很可能根本不懂执行计划,更不要说statspack了。而我认为,数据库优化,应该首先从大的方面考虑:网络、服务器硬件配置、操作系统配置、Oracle服务器配置、数据结构组织、然后才是具体的调整。实际上网络、硬件等往往无法决定更换,应用程序一般也无法修改,因此应该着重从数据库配置、数据结构上来下手,首先让数据库有一个良好的配置,然后再考虑具体优化某些过慢的语句。我在给我的用户系统进行优化的过程中,总结了一些基本的,简单易行的办法来优化数据库,算是我的三板斧,呵呵。不过请注意,这些不一定普遍使用,甚至有的会有副作用,但是对OLTP系统、基于成本的数据库往往行之有效,不妨试试。(注:附件是Burleson写的用来报告数据库性能等信息的脚本,本文用到)
一.设置合适的SGA
常常有人抱怨服务器硬件很好,但是Oracle就是很慢。很可能是内存分配不合理造成的。
(1)假设内存有512M,这通常是小型应用。建议Oracle的SGA大约240M,其中:共享池(SHARED_POOL_SIZE)可以设置60M到80M,根据实际的用户数、查询等来定。数据块缓冲区可以大致分配120M-150M,8i下需要设置DB_BLOCK_B?RS,DB_BLOCK_B?R*DB_BLOCK_SIZE等于数据块缓冲区大小。9i 下的数据缓冲区可以用db_cache_size来直接分配。
(2)假设内存有1G,Oracle 的SGA可以考虑分配500M:共享池分配100M到150M,数据缓冲区分配300M到400M。
(3)内存2G,SGA可以考虑分配1.2G,共享池300M到500M,剩下的给数据块缓冲区。
(4)内存2G以上:共享池300M到500M就足够啦,再多也没有太大帮助;(Biti_rainy有专述)数据缓冲区是尽可能的大,但是一定要注意两个问题:一是要给操作系统和其他应用留够内存,二是对于32位的操作系统,Oracle的SGA有1.75G的限制。有的32位操作系统上可以突破这个限制,方法还请看Biti的大作吧。
二.分析表和索引,更改优化模式
Oracle默认优化模式是CHOOSE,在这种情况下,如果表没有经过分析,经常导致查询使用全表扫描,而不使用索引。这通常导致磁盘I/O太多,而导致查询很慢。如果没有使用执行计划稳定性,则应该把表和索引都分析一下,这样可能直接会使查询速度大幅提升。分析表命令可以用ANALYZE TABLE 分析索引可以用ANALYZE INDEX命令。对于少于100万的表,可以考虑分析整个表,对于很大的表,可以按百分比来分析,但是百分比不能过低,否则生成的统计信息可能不准确。可以通过DBA_TABLES的LAST_ANALYZED列来查看表是否经过分析或分析时间,索引可以通过DBA_INDEXES的LAST_ANALYZED列。
下面通过例子来说明分析前后的速度对比。(表CASE_GA_AJZLZ大约有35万数据,有主键)首先在SQLPLUS中打开自动查询执行计划功能。(第一次要执行/RDBMS/ADMIN/utlxplan.sql来创建PLAN_TABLE这个表)
SQL> SET AUTOTRACE ON
SQL>SET TIMING ON
通过SET AUTOTRACE ON 来查看语句的执行计划,通过SET TIMING ON 来查看语句运行时间。
SQL> select count(*) from CASE_GA_AJZLZ;
COUNT(*)
----------
346639
已用时间: 00: 00: 21.38
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (FULL) OF 'CASE_GA_AJZLZ'
……………………
请注意上面分析中的TABLE ACCESS(FULL),这说明该语句执行了全表扫描。而且查询使用了21.38秒。这时表还没有经过分析。下面我们来对该表进行分析:
SQL> analyze table CASE_GA_AJZLZ compute statistics;
表已分析。
已用时间: 00: 05: 357.63
然后再来查询:
SQL> select count(*) from CASE_GA_AJZLZ;
COUNT(*)
----------
346639
已用时间: 00: 00: 00.71
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=351 Card=1)
1 0 SORT (AGGREGATE)
2 1 INDEX (FAST FULL SCAN) OF 'PK_AJZLZ' (UNIQ) (Cost=351
Card=346351)
…………………………
请注意,这次时间仅仅用了0.71秒!这要归功于INDEX(FAST FULL SCAN)。通过分析表,查询使用了PK_AJZLZ索引,磁盘I/O大幅减少,速度也大幅提升!下面的实用语句可以用来生成分析某个用户的所有表和索引,假设用户是GAXZUSR:
SQL> set pagesize 0
SQL> spool d:/analyze_tables.sql;
SQL> select 'analyze table '||owner||'.'||table_name||' compute statistics;' from dba_tables where owner='GAXZUSR';
SQL> spool off
SQL> spool spool d:/analyze_indexes.sql;
SQL> select 'analyze index '||owner||'.'||index_name||' compute statistics;' from dba_indexes where owner='GAXZUSR';
SQL> spool off
SQL> @d:/analyze_tables.sql
SQL> @d:/analyze_indexes.sql
解释:上面的语句生成了两个sql文件,分别分析全部的GAXZUSR的表和索引。如果需要按照百分比来分析表,可以修改一下脚本。通过上面的步骤,我们就完成了对表和索引的分析,可以测试一下速度的改进啦。建议定期运行上面的语句,尤其是数据经过大量更新。
当然,也可以通过dbms_stats来分析表和索引,更方便一些。但是我仍然习惯上面的方法,因为成功与否会直接提示出来。
另外,我们可以将优化模式进行修改。optimizer_mode值可以是RULE、CHOOSE、FIRST_ROWS和ALL_ROWS。对于OLTP系统,可以改成FIRST_ROWS,来要求查询尽快返回结果。这样即使不用分析,在一般情况下也可以提高查询性能。但是表和索引经过分析后有助于找到最合适的执行计划。
三.设置cursor_sharing=FORCE 或SIMILAR
这种方法是8i才开始有的,oracle805不支持。通过设置该参数,可以强制共享只有文字不同的语句解释计划。例如下面两条语句可以共享:
SQL> SELECT * FROM MYTABLE WHERE NAME='tom'
SQL> SELECT * FROM MYTABLE WHERE NAME='turner'
这个方法可以大幅降低缓冲区利用率低的问题,避免语句重新解释。通过这个功能,可以很大程度上解决硬解析带来的性能下降的问题。个人感觉可根据系统的实际情况,决定是否将该参数改成FORCE。该参数默认是exact。不过一定要注意,修改之前,必须先给ORACLE打补丁,否则改之后oracle会占用100%的CPU,无法使用。对于ORACLE9i,可以设置成SIMILAR,这个设置综合了FORCE和EXACT的优点。不过请慎用这个功能,这个参数也可能带来很大的负面影响!
四.将常用的小表、索引钉在数据缓存KEEP池中
内存上数据读取速度远远比硬盘中读取要快,据称,内存中数据读的速度是硬盘的14000倍!如果资源比较丰富,把常用的小的、而且经常进行全表扫描的表给钉内存中,当然是在好不过了。可以简单的通过ALTER TABLE tablename CACHE来实现,在ORACLE8i之后可以使用ALTER TABLE table STORAGE(B?R_POOL KEEP)。一般来说,可以考虑把200数据块之内的表放在keep池中,当然要根据内存大小等因素来定。关于如何查出那些表或索引符合条件,可以使用本文提供的access.sql和access_report.sql。这两个脚本是著名的Oracle专家 Burleson写的,你也可以在读懂了情况下根据实际情况调整一下脚本。对于索引,可以通过ALTER INDEX indexname STORAGE(B?R_POOL KEEP)来钉在KEEP池中。
将表定在KEEP池中需要做一些准备工作。对于ORACLE9i 需要设置DB_KEEP_CACHE_SIZE,对于8i,需要设置b?r_pool_keep。在8i中,还要修改db_block_lru_latches,该参数默认是1,无法使用b?r_pool_keep。该参数应该比2*3*CPU数量少,但是要大于1,才能设置DB_KEEP_CACHE_B?R。b?r_pool_keep从db_block_b?rs中分配,因此也要小于db_block_b?rs。设置好这些参数后,就可以把常用对象永久钉在内存里。
五.设置optimizer_max_permutations
对于多表连接查询,如果采用基于成本优化(CBO),ORACLE会计算出很多种运行方案,从中选择出最优方案。这个参数就是设置oracle究竟从多少种方案来选择最优。如果设置太大,那么计算最优方案过程也是时间比较长的。Oracle805和8i默认是80000,8建议改成2000。对于9i,已经默认是2000了。
六.调整排序参数
(1) SORT_AREA_SIZE:默认的用来排序的SORT_AREA_SIZE大小是32K,通常显得有点小,一般可以考虑设置成1M(1048576)。这个参数不能设置过大,因为每个连接都要分配同样的排序内存。
(2) SORT_MULTIBLOCK_READ_COUNT:增大这个参数可以提高临时表空间排序性能,该参数默认是2,可以改成32来对比一下排序查询时间变化。注意,这个参数的最大值与平台有关系。
七.调整其它几个关键的性能参数
很多人认为使用oracle数据库,系统的默认参数就是最好的,其实不是这样
====================================================================================================
● 配置和优化有什么不同
● 获得最大的性能
● 配置操作系统
● 配置Oracle
Oracle 性能
● 调整和配置数据库对象
● 优化Oracle
最大化
如果你问很多Oracle DBA“你工作中最大的一部分是什么?”几乎所有的回答都是“数据库的配置和优化。”Oracle 是一种真正复杂和强大的产品,而且它的强大的能力在于它对每个单独的数据库配置都可以以最好的性能运行。本章讲述我们配置和优化 Oracle 数据库的方法,并提供了为站点实现一个高性能数据库的指导方针。
大多数Oracle DBA 连续的、每天的职责是使 Oracle 数据库获得可能的最好性能。对于“性能”可能有许多定义,但是我们把性能定义为目标和在怀疑有问题的数据库中执行一个典型操作需要的可以测量的时间。是的,这是一个太简单的定义,它忽视了其他的测量尺度,如资源使用。但是让我们正视它:我们期望数据库尽可能地快,因此为了这个目的这是一个合理的定义。
整本书都是以Oracle 性能为主题写的(参见附录“DBA 使用的资源”以查看我们认为你应该注意的内容,注1),所以我们不能在一章中就阐述完复杂的Oracle 性能优化,我们希望提供一种直截了当的性能优化方法并提供能应用到各个不同安装上的实际指南。
从物理和逻辑的实现、处理的事务类型及这些事务的性能需求方面来看,每个Oracle 安装都是不同的,认识到这点很重要。结果是虽然一些厂商(包括 Oracle) 尝试提供,但仍没有一种自动的优化方法,而且也没有单一的一套规则可以提供一种使数据库性能最优的方法。然而,我们可以提供一种方法,在适当应用并结合DBA 知识和经验的情况下,该方法将使任何给定的数据库有好的性能。
注1: 我们尤其推荐Mark Gurry 和Peter Corrigan 的《Oracle Performance Tuning》第二
版(O'Reilly&Associates,1997)。
46
配置和优化有什么不同
使一个 Oracle 数据库获得最佳性能需要认真注意数据库的配置和优化两方面。这些术语经常交换使用,但是事实上,它们是两个不同的任务,无可否认的是在它们之间有一少部分内容是重叠的。
配置是设置数据库的物理和逻辑组件的过程,也是配置主机系统的过程,而优化是修改数据库的内部行为的过程,以便操作以特定方式运行。整个过程某种程度上是循环的,因为合适的优化经常包含修改配置,然后再一次查看优化结果。图 3-1 演示了配置和优化过程的基本步骤。
可以配置什么
能在一个 Oracle 数据库中配置的一些项目如下:
● 影响系统进程分配的数据库的组件,例如 : SQL*Net MTS(Multi-Threaded Server,多线程服务器)
并行查询(Parallel Qry)
并行服务器(Parallel Server)
● 物理存储的布局和大小
● 数据库对象的大小,如:
表
索引
回滚段
排序区
临时表空间
重做日志
不是
图 3-1:配置和优化过程
分区表
惟一索引(Index-only)表
● 内存的数量和分配,例如 :
数据库缓冲区
重做日志缓冲区
共享池
可以优化什么
Oracle 数据库可以优化的方面包括下列各项:
● 内存使用
● 磁盘使用
● SQL 语句执行
获得最大的性能
使你的Oracle数据库获得最大的性能并不是一下子就可以做到的,这通常是大量辛苦的工作、思考和计划的结果。然而,从付出努力所得到的回报来看,是非常值得的,你的数据库在最高效地运行,你的用户高兴,你也很满意。
我们使性能最大化的方法是按自然层次分类的。需要从3 个不同方面,并按照顺序来阐述。这3 个方面是:
● 操作系统配置
● Oracle 资源配置
● 对象创建和SQL 语句执行
这些方面不是互不相关的,实际上,对某方面的重要改变可能需要考虑其他方面。它们是顺序依赖的,也就是说,直到你已正确配置和调整了操作系统,你才能使Oracle 达到很好的性能。同样,查询的快速执行取决于是否合理地配置了Oracle 环境。
每个Oracle数据库的情况都是不同的,因此我们不能精确地告诉你该如何完成你的配置和优化目标,甚至你的目标是什么。我们要做的是为你提供一个我们已经成功的方法。
配置操作系统
这通常是容易的,因为那不是你的工作(在大部分情形下)! 在大多数安装中,有系统管理员或管理者负责操作系统和硬件事情。这个系统管理员通常是硬件和操作系统软件方面的专家,而且大多数DBA不用再管它。但在服从系统管理员专长的同时,这里有你必须确定的几点:
● 应该充分利用物理内存,但是交换(swapping)(在交换内存环境中)不应该发生。把内存交换到磁盘的过程非常慢,因此如果系统需要更多的内存,就再买一些内存。尤其是要确定你没有创建对于物理内存来说太大的SGA,因为SGA 交换将严重降低 Oracle 性能
● CPU 在峰值时应达到100% 使用,但是进程不应该等待CPU
● 磁盘和控制器应该运行在最佳容量(通常是最大值的60%~90% 或靠近最佳容量),而且没有输入/ 输出等待。作为一个DBA,你也有一些对这个区域的控制,我们将会在本章后面描述
● 网络传输量不应该是一个瓶颈。考虑用主干网络把服务器连接在一起,并且如有可能把客户机/ 服务器通信和服务器/ 服务器通信分开
● 尽量把Oracle 服务器放在一个单纯的机器上,把用户放到另外的机器上
● 确保安装了任何可能影响Oracle 的操作系统组件(包括补丁)
因为Oracle 是数据库市场中的一个主要提供商,所以大多数硬件提供商中都有Oracle“专家”的职员,他们能提供可能影响Oracle 运行的硬件和操作系统方面的建议。要充分利用这些专业意见。
配置Oracle
Oracle 的总性能受所安装的组件以及这些组件如何配置的影响。Oracle 数据库的高性能对于从运行在数据库的事务中获得最大性能是很必要的。这一节为配置SQL*Net/Net8、MTS、并行查询(Parallel Qry)和并行服务器(Parallel Server) 提供了一般配置指南和一些具体建议。
配置指南
尽管每次安装都会不同,但总有一些能应用到大多数数据库的普遍适用的配置指南,而不管安装组件的不同和数据库应用的不同。下面章节将描述这些通用的指南。
查阅文档
这看起来显而易见,但还是有必要说。即使有经验的DBA 也会从开始Oracle 安装之前的快速阅读相关文档中受益。我们推荐你(至少)查阅下列文档:
● 特定于硬件的IUG(安装和用户指南)
● 服务器管理员指南
● 版本发布说明(通常打包在介质中)
● README 文件,通常可以在安装介质中找到,它包含印刷文档中可能没有的最新信息
检查资源需求
在开始安装之前,确认是否有足够的系统资源。与使用平台相关的IUG 资料包含了有关磁盘存储和内存需求的全面信息。记住这些需求是最小需求,而实际上所需资源可能要更大,这与你做的其他配置有关。例如,如果你定义较大的SGA,就需要更多的内存。
尤其是要确保在你安装Oracle 软件(一般称为ORACLE_HOME)的设备上有足够的磁盘空间,以安装所有的软件和辅助文件。
检查系统特权
大多数操作系统要求执行Oracle安装的账户有特定的权限。一定要查看IUG进行确认,而且一定要确认系统管理员已经正确地进行了设置。注意这些特权可能包括在特定设备上创建目录和文件的权利。
确定控制文件所在位置
Oracle 要求至少有一个控制文件。你应该设置至少两个(通常更多)控制文件。这点极其重要,因为如果控制文件的所有拷贝丢失,你将不能挂接数据库。因此要把控制文件放在不同的磁盘上,如有可能放置在不同的磁盘控制器上。
SQL*Net 配置
要对SQL*Net(Oracle7)和 Net8(Oracle8)进行配置,通常使用Oracle 网络管理器或Net8助手。这通常在数据库软件安装后,并且至少有一个Oracle 实例运行后进行,但是配置应该预先计划好。在开始SQL*Net/Net8 配置之前,你应了解如下内容:
● 网络协议的类型:用来在你所在的环境中访问Oracle
● 命名模式:用来识别 Oracle 网络节点
● 你所在环境中的所有服务器、网关和多协议交换的名称和网络位置
一旦配置了SQL*Net/Net8,就要在每个服务器上设置如下文件:
listener.ora
控制SQL*Net 监听进程的操作
tnsnames.ora
在未使用Oracle 命名软件时,维护网络中逻辑节点名称(别名)和物理位置之间的关系
sqlnet.ora
控制Oracle 网络操作的登录(不是必需但强烈要求)
如果你使用多线程服务器,也需要在INIT.ORA 文件中进行配置,如下小节所示。
MTS 的配置
MTS(多线程服务器)在INIT.ORA 文件中进行配置,INIT.ORA 样本的参数设置如下所示:
mts_dispatchers="ipc,1"
mts_dispatchers="tcp,1"
mts_max_dispatchers=10
mts_servers=1
mts_max_servers=10
mts_service=TEST
mts_listener_address="(ADDRESS=(PROTOCOL=ipc)(KEY=TEST))"
mts_listener_address="(ADDRESS=(PROTOCOL=tcp)(HOST=10.74.72.42)(PORT=1526))"
这个例子将配置一个MTS,它将处理与TEST 数据库的TCP/IP 连接。它最多将启动10 个调度程序,而且将创建多达10 个服务器进程。
注意: 记住,每个MTS进程都占用在INIT.ORA参数进程中指定总数中的数量,而且占用在操作系统级为Oracle 用户开放最大的进程数中的数量。
并行查询的配置
PQO(并行查询选项)是 Oracle 的一个强大的特性,为了正确地使用它,一定要合理配置数据库。并行查询允许多CPU 系统把数据库任务(通常是全表扫瞄)划分为能同时(并行)执行的一些片。为执行该任务要求如下:
● 通过设置INIT.ORA 中的PARALLEL_MAX_SERVERS 参数为一个大于0 的值来使能多个并行进程
● 创建表空间必须使用多个数据文件,数据文件要分配到不同设备上。理论上讲,分配给每个表空间的设备数等于系统中CPU 的数量
● 利用并行查询的表应该将其并行度值(使用CREATE TABLE 语句中的PARALLEL 子句)设置为包含表空间(表在其中创建)的数据文件的数目
并行服务器的配置
为了使用OPS,并行服务器允许由多个Oracle 实例共享一个 Oracle 数据库,你可以通过在每个参与实例中使用INIT.ORA 参数来设定并行服务器特性,这些参数包括:
PARALLEL_SERVER
一定要设为TR,以使能OPS(只对于 Oracle8)。
INSTANCE_NUMBER
标识数据库的实例。
ROLLBACK_SEGMENTS
指定每个实例私用的回滚段。也可以指定公用的回滚段,但是这不是必需的。
THREAD
识别与实例相关的重做日志线程。
GC_DB_LOCKS
实例锁总数(仅在Oracle7 中)。
GC_FILES_TO_LOCKS
数据库文件锁的数目。
GC_LCK_PROCS
分布锁的总数。
GC_ROLLBACK_LOCKS
回滚锁的总数。
GC_SAVE_ROLLBACK_LOCKS
回滚保存锁的数目(仅在Oracle7 中)。
GC_SEGMENTS 有影响空间管理行为的段的最大数目,该空间管理行为同时在段上执行(仅在Oracle 7 中)。
INSTANCE_GROUPS
把实例指定给一个或多个指定组(仅在Oracle8 中)。
LM_LOCKS
为锁管理器配置的锁数目(仅在Oracle8 中)。
LM_PROCS
锁管理器的进程数(仅在Oracle8 中)。
LM_RESS
能被每个锁管理器实例锁定的资源数目(仅在Oracle8 中)。
OPS_ADMIN_GROUP
把实例分配给一个组来监视(仅在Oracle8 中)。
PARALLEL_INSTANCE_GROUP
标识要用来产生并行查询从属的并行实例组(仅在Oracle8 中)。
ROW_LOCKING
应该总被设定为ALWAYS。
SERIALIZABLE
应该设定成FALSE(仅在Oracle7 中)。
SINGLE_PROCESS
应该设定成FALSE(仅在Oracle7 中)。
关于这些参数的详细信息参见第十二章“初始化参数”。因为OPS 是一个非常复杂的产品,所以你应该在尝试配置并行服务器环境之前查阅《Oracle Parallel Server Concepts》和《Administration Guide》。在做配置时,记住以下几点:
● 在 Unix 平台上,所有的数据文件一定要创建到裸分区中
● 当创建一个数据库时,只自动创建重做线程1,额外的线程需要显式建立,而且你应指明重做日志属于哪个线程
● 虽然不是必需的,但确保实例数目和线程数目相同将避免混淆
注意: 术语“并行查询”和“ 并行服务器”经常被混淆。并行查询指单个Oracle 实例把操作(比如一个全表扫描)在相同主机上的多CPU 间分布并且合并结果的能力。另一方面,并行服务器是多个在不同主机上的Oracle实例共享一个物理数据库的特性。在这种情况下,工作是通过把用户在多个实例间分布或通过在多实例间产生并行查询进程来在Oracle 实例间分布的。
调整和配置数据库对象
要获得最大的数据库性能,数据库对象的合理大小和配置是非常重要的。对象的合理大小是一个不断进行的工作,随着不断创建对象和修改对象,也需要不断地检查对象特性并在需要时将其改变。以下与调整大小相关的问题的数值与性能成反比:
表空间碎片(tablespace fragmentation) 表空间碎片使许多无法使用的小延伸区分散在表空间中。当创建对象时,如果延伸区的INITIAL 或NEXT 的值设置不合理就会产生碎片。
行链(row chaining) 这个问题将导致单行的数据驻留在多个Oracle块中,典型情况发生在PCTFREE 设置不足且表不断进行更新时。
多个延伸区(multiple extent) 多个延伸区,可能导致一个特定对象的数据分散在一个或几个数据文件之中,这是由在创建对象时指定了不合适的INITIAL或NEXT延伸区大小而引起的。这个问题可能会在MAXEXTENTS 参数被允许为默认值时变得很严重,因为尝试分派一个超过那个数目的延伸区将导致失败。
日志等待当写日志缓冲区记录到一个日志文件或当日志文件切换时,日志等待将引起一个进程等待,这时日志等待能大大增加处理时间。这通常由日志文件数目太少和日志文件太小等原因引起。
扩展一个回滚段失败这样的失败(能引起一个事务回滚)是由于没有分配充足的回滚段数目,或者是由于分配的回滚段不够大。
下列各节讲述了可以避免这些性能问题的一些指南和建议。
表
表是 Oracle 数据库中数据存储的基本单位,因此表的配置和由此产生的性能将对数据库总体性能产生很大的影响。下面是表配置的一些指导方针:
● 试着估计一个表将多大,并且分配一个足够大的初始延伸区来存放整个表。然而,如果你正在使用并行查询,则应跨越不同的数据文件来分配总的空间,使分配的延伸区数目和表的并行度相等
● 考虑使用多个表空间,每个表空间对应于不同大小或类型的表。例如,你可能有3 个表空间:LARGE_DATA、MEDIUM_DATA 和SMALL_DATA,每个会用来存储大小不同的表。如果你使用多个表空间,要确保把每个表分配到恰当的表空间中
● 确保分配一个DEFAULT TABLESPACE 给每个用户。如果没有分配,Oracle 将使用SYSTEM 表空间作为默认值
● 如果可能,保证INITIAL和NEXT 延伸区大小总是相同大小的单元的整数倍,例如,是 512K 的整数倍。这样,延伸区将是统一的大小,而且会比较容易分配额外的延伸区,而不引起表空间碎片。如果可能,在一个表空间中可以使用大小相同的延伸区
● 设定PCTINCREASE参数为 0,为了防止延伸区分配失控和保持统一的延伸区大小。
● 设定MAXEXTENTS 参数为UNLIMITED。这将防止延伸区用完,因为多个延伸区对它们产生很小的性能影响(虽然广泛分布的延伸区对性能有负面影响)。这样做可以防止错误,但是不要把它作为INITIAL 大小的替代
● 如果表没有更新,则设定PCTFREE 为0。如果表有更新,则估计行的列的增长程度,并分配一个PCTFREE 来防止块链接,而在块中没有过多的未使用空间
● 如果有很多事务同时访问表,将INITRANS 设定为一个大于1(默认的)的数
● 将 MAXTRANS 设定为在预期表上同时访问的最大数目。一个较小值将会导致一个或多个事务等待前一个事务完成
索引
正确使用索引可以使性能大大提高,这是任何Oracle 单一特性所不能做到的。虽然许多性能提高获益于优化 SQL 语句(见第八章“查询优化”),但我们也提供一些配置指南:
● 为索引创建一个单独的表空间,并且保证这个索引表空间的数据文件与包含索引表的表空间的数据文件不在同一个磁盘上
● 试着去估计索引的大小而且分配一个足够的INITIAL延伸区来存储整个索引,除非你正在使用并行查询,否则在这种情况下,你应该在与索引的并行度相同的数据文件之间分配总的空间
● 如果可能,保证INITIAL和NEXT 延伸区大小总是相同大小的单元的整数倍,例如,是 512K 的整数倍。这样,延伸区将会是统一的大小,而且会比较容易分配额外的延伸区而不引起表空间碎片
● 设定 PCTINCREASE 参数为0 以防止延伸区分配失控并保持统一的延伸区大小
● 设定 MAXEXTENTS 参数为UNLIMITED。这将防止延伸区用完,而多个延伸区可能产生的性能影响很小(虽然广泛分布的延伸区对性能有负面影响)。这样做可以防止错误,但是不要把它作为INITIAL 大小的替代
回滚段
Oracle 用回滚段来维护数据的一致性,允许事务的取消或回滚。回滚段使用很多的输入/ 输出,下面是配置回滚段的一些指导方针:
● 为回滚段创建一个单独的表空间,如果可能,把这个表空间的数据文件放在一个与其他数据文件不同的磁盘上
● 永远不要在SYSTEM 表空间中创建回滚段(除了在数据库创建期间需要的临时回滚段以外,见第二章“安装”)
● 确保为回滚表空间分配了足够大的空间,以允许回滚段为了适应大的更新事务来按照需要增长空间。记住批事务容易产生很大的回滚段
● 总是保持回滚段的INITIAL 和NEXT 延伸区使用相同的数值(在CREATE TABLESPACE 语句中的DEFAULT STORAGE 子句中定义)。为回滚段分配大小相等的块可以防止空间碎片
● 记得每个回滚段必须至少有两个延伸区,因此段的初始大小实际上是INITIAL + NEXT 的总和
● 定义一个OPTIMAL值,以便使为了延伸适应一个大事务而增长的回滚段可以回缩到一个合理的大小。然而,不要让这个值太小,否则会浪费时间来为回滚段分配额外的延伸区
排序区
Oracle 使用INIT.ORA 参数SORT_AREA_SIZE 来为数据排序分配内存。当一个排序不能够在内存中完成时,Oracle 使用数据库中的临时段,但这非常慢。应当小心平衡SORT_AREA_SIZE,因为大的排序区可以通过减少输入/ 输出来显著增加性能,但是这将用光内存并引起分页。
注意: 记住这个参数应用到每个用户进程。每个执行排序的用户进程都将分配 SORT_AREA_ SIZE 内存。因此,如果 SORT_AREA_SIZE 被设定为/MB,而有100 个用户进程正在执行排序,那么将分配总数为100MB 的内存。
临时表空间
如果没有为执行排序的用户进程分配足够的内存,那么Oracle 将通过为用户在TEMPORARY TABLESPACE参数指定表空间中创建临时段来在磁盘上执行排序。除此之外,临时段用来执行复杂查询,如连接、UNION、MINUS 和索引创建。临时区的指南如下:
● 为临时段创建一个单独的表空间(通常叫做 TEMP),如果可能,把这个表空间对应的数据文件放在一个单独的磁盘上
● 在CREATE TABLESPACE命令的DEFAULT STORAGE子句中指定INITIAL 和NEXT 参数。把两者的值设为相等,以消除空间碎片,在 TEMP 表空间中极易产生碎片,因为在那里不断地创建并删除对象
● 要确保为每个用户指定一个TEMPORARY TABLESPACE。如果没有指定,Oracle 将把SYSTEM 作为默认的表空间,而这样对性能有负面的影响
重做日志
重做日志,也称联机重做日志文件,对 Oracle 的失效恢复能力至关重要。重做日
志的适当配置不但对数据库的总体性能很关键,而且对恢复数据库的能力也很重要(见第四章“防止数据丢失”)。相关指南如下:
● 使用Oracle 内嵌的镜像特性,把重做日志文件的多组放在不同的磁盘上
● 分配足够的重做日志文件以便Oracle 无须为了重复使用一个文件而等待它。Oracle 至少要求有两个重做日志文件,但是4 个或更多个是必要的
● 分配的重做日志文件要足够大以防止太多的日志文件切换,但又要适当的小以保证当前联机日志文件失效时很好地恢复。如果文件较小,可能恢复已经归档的所有事务,而大的日志文件使数据库有可能丢失更多的事务
● 设置INIT.ORA参数 LOG_CHECKPOINT_INTERVAL值大于重做日志文件的大小,这样将避免检查点(checkpoint)进程,直到日志文件满为止(引起一个检查点进程)。这个参数以数据库块来表达
警告: 记住一个日志切换将导致从SGA 将脏缓冲区(例如有更新)写入到磁盘。
● 如果你正在运行Oracle7,考虑设定INIT.ORA 参数CHECKPOINT_PROCESS 为TR。这么做将创建一个执行检查点进程的单独进程,而并非由LGWR(日志写入进程)处理。见第十章“Oracle 实例”,可以了解更多信息
归档日志目的地
在配置方面一个经常需要注意的问题是要保证归档日志目的地有足够的空间。如果数据库正在归档日志模式中运行,那么当一个联机重做日志文件填满时,Oracle 的ARCH进程将复制这个文件的内容到INIT.ORA参数ARCHIVE_LOG_DEST指定的目录。如果目的地太小,ARCH 就不能复制日志文件,而一旦所有的联机日志文件满,整个数据库就会停止,直到这个问题解决。有经验的DBA 已经意识到这种情况,这种情况大多数在半夜发生,就像REM 休眠一样。
优化 Oracle
或许DBA 的工作没有哪一方面能像优化这样消耗时间。成功的Oracle 优化既要求知识又要求经验,挑战和挫败也同时存在。整卷都在写Oracle优化(参见附录“DBA 使用的资源”),但我们不能在一节中包括优化的所有方面。相反,正如我们前面提到的,我们将列出可以应用到各种情况的优化方法的大纲。
结构化优化方案
Oracle 数据库的成功优化需要仔细的、有规则的方案。像整体系统配置一样,优化一定要包括下列各项:
● 硬件和操作系统性能● Oracle 实例性能
● 单独的事务(SQL)性能
这些方面应该按顺序进行,因为没有硬件和操作系统的良好优化,Oracle 的性能优化是不可能的。没有数据库的有效运行,一个单独的SQL语句不可能很好地被优化。优化这些方面中的任何一方面时,都包括3 个步骤:
1. 测量目前的性能。
2. 做适当的变化。
3. 评估结果。
警告:对Oracle实例的一些改变可能引起对操作系统环境的改变。比如,分配附加的数据库缓冲区可能导致操作系统开始分页,而这可能要求额外的操作系统优化来消除。
优化进程几乎总是反复的。也就是说,在完成3 个步骤之后,DBA 必须回到第一步骤并且重复这个过程。这将一直持续到不会再有性能改善为止。
Oracle 实例优化
Oracle 实例层的大多数性能的提高将通过两个方面达到: 内存使用和磁盘输入/ 输出。
内存使用
基于内存的操作比磁盘操作快得多(有时成千上万倍),这一点是不值得惊讶的。结果将导致用内存访问数据来代替磁盘输入/ 输出,以使性能得到巨大的提高。相关的3 个主要方法描述如下:
分配额外的DB_BLOCK_B?RS 这或许是改善总体性能的单一的最有效的方法,特别是在查询上。更多的数据库缓冲区允许更多的数据块数据保持在内存中,因此可以按内存速度访问包含在这些块中的数据,而不需要磁盘输入/ 输出。缓冲区是由INIT.ORA 参数
DB_BLOCK_B?RS 来分配的,它的数值是要分配的数据块缓冲(block b?r)数目。因此,如果数据库块大小是8192,每个DB_BLOCK_B?R 将是8192 字节。注意改变DB_BLOCK_B?RS 值后直到下次数据库重启才生效。
注意: 注意不要分配太多的DB_BLOCK_B?RS以导致操作系统开始分页,分页将消除你获得的性能,而且将对整体性能产生负面影响。
分配额外的共享池共享池大小由INIT.ORA的参数SHARED_POOL_SIZE 控制,它指定了以字节为单位的共享池大小。共享池的主要内容是字典缓存区(dictionary cache)和共享SQL 区(shared SQL area)。由于字典缓存区的各部分组件由Oracle 自动分配,所以共享池的任何增加都会使字典缓存区和共享SQL 区增加。
共享SQL 区包含最近执行的SQL 语句的拷贝,连同相关信息,如它们的执行计划。共享池越大,特定SQL 语句被解析并且驻留在共享SQL 区就越有可能,因此节省了需要再次处理这个语句的时间。在相同的SQL语句被多次执行且对速度有要求的事务处理系统中,这是个特别重要的值。
分配更多的日志缓冲区空间日志缓冲区是用来存储将要写到联机重做日志文件上的数据的。日志缓冲区的大小由INIT.ORA 参数LOG_B?R 控制,其数值以字节表示。为日志缓冲区分配更多的内存,将减少磁盘输入/ 输出,尤其是在事务特别长或数量很多时。
磁盘输入/ 输出
磁盘访问是任何计算机系统上的最慢的操作。作为一个数据库系统,Oracle 的存储和访问数据非常依赖磁盘访问。考虑一个典型的更新一个表的一行的SQL 语句,将发生下列各项操作:
1. 读数据字典获得关于表和正在被操作的行的信息。
2. 读适当的索引来定位要更新的行。
3. 读包含行的数据块。
4. 写回滚信息到一个回滚段。
5. 写更新信息到联机日志文件。
6. 重写数据块。
7. 重写索引块。
虽然一些操作可以通过有效使用内存消除,正如我们前面提到的,但所有这些操作都潜在地要求磁盘输入/ 输出。通过尽可能使磁盘输入/ 输出有效,可增强总性能。使磁盘输入/ 输出最大值的基本指南如下:
● 只要可能,分离输入/输出操作到单独的磁盘上。这样,在执行另外一个时,不需要等待一个磁盘操作完成。例如,如果回滚段和日志文件在相同的磁盘上,需要写回滚记录,然后磁盘磁头需要移动到日志文件记录要写的那部分磁盘。这是非常耗时的
● 把高输入/ 输出的磁盘放在不同的控制器上。现代的控制器可以处理有限数目的并发操作,但是尽可能使用更多的控制器将消除任何控制器等候并加速性能
● 把最忙的文件和表空间(例如,日志文件、回滚段、一些索引)放在最快的可用磁盘上
关于RAID 的注释
磁盘技术的最新发展使 RAID(廉价磁盘冗余阵列)成为许多系统上很常用的一个选项。通常,当使用术语 RAID 时,硬件管理员会立刻想到RAID 5(或 RAID-5), 它允许多个磁盘结合成一个大的设备。通过分配一个磁盘设备来存储冗余数据,一个 RAID-5 磁盘阵列可以承受阵列中的任何单一磁盘失效,并且经常是热交换的,也就是当一个磁盘失效时,其他的磁盘继续工作的同时更换这块磁盘,而不必关闭系统。
事实上RAID-5 是非常强大且廉价的。当配置Oracle 数据库时,在大多数情况下要避免使用这种技术。这可能听起来很刺耳,但是事实上虽然RAID-5 成本较低,而且提供了很好的数据保护级别,但是它的磁盘输入/输出花费很高。尤其是在RAID5 阵列上的写操作要比在单一磁盘上的写操作慢得多。RAID-5 阵列的一个好的替代品是RAID1,就是大家都知道的磁盘镜像。虽然比RAID-5(一半磁盘用来存储冗余数据)更贵,但是RAID-1提供了完全的数据保护,而在输入/ 输出效率上没用降低。
警告: RAID-1 要求有足够的硬件资源。尤其是由于每次写操作实际都要对磁盘进行两个写操作,所以控制器上的负荷是非RAID 的两倍。
现在性能最好的RAID 是RAID-0+1,有时叫做 RAID-10。RAID 的这个级别在多个驱动器上结合了带有数据条带(RAID-0)的镜像磁盘(同RAID-1),这可以消除等待磁盘头定位的延迟。而这在其他的RAID 控制器中都没有,RAID-0+1 是值得考虑的。
操作系统条带化
许多操作系统提供了在多个磁盘设备中磁盘扇区的自动条带化。条带化允许磁盘输入/ 输出连续,而没有头定位的延迟。虽然这个技术提供了比在一个单一磁盘上更好的性能,但也有一些缺点: 结合多个磁盘为一个单一的条带单元意味着DBA不能够控制单个文件在单独磁盘设备的位置。如果你的系统上只有少数的大磁盘,你应该考虑操作系统条带化,但是多磁盘设备或多 RAID-0+1 阵列通常会从Oracle 产生更好的性能。
Oracle 条带化
作为DBA,通过小心地把数据文件分配到单个磁盘设备或 RAID-0+1阵列,你可以达到与操作系统条带化相似的结果。例如,建立跨越4 个磁盘的Oracle 条带化需做下列各项工作:
● 创建一个有4 个数据文件的表空间,每个位于一个不同的磁盘设备上
● 在表空间中创建对象,指定MINEXTENTS 4。Oracle 将把4 个延伸区分配到4个数据文件上,这样就实现了条带化。这个行动不是自动的,还需使用ALTER TABLE ... ALOCATE EXTENT 命令
Oracle 条带化技术非常强大,尤其和并行查询结合的时候,将通过多CPU 处理查询过程。
SQL 优化
假如主机服务器和操作系统正在你的站点顺利运行,而且你配置并优化了Oracle, 使其在最好状态运行,但是你的重要应用程序仍然运行很差。不幸的是这种情况经常发生。解决方法是通过检查和优化正在被执行的SQL 语句来优化应用程序。
SQL 优化这个题目值得写一本书。实际上,在市场上有很不错的书阐述得比这里更为详细。我们建议你检查附录中列出的DBA资源。在这小节中,我们将为你提供优化SQL 语句的一些概要建议和指南。
查询处理
第八章“查询最优化”描述了Oracle 如何为一个特定的SQL 语句创建一个计划。Oracle 现在用两个方法中的一个来决定该如何执行一个SQL 语句:
基于规则的方法应用一个标准的、固定的(但是经常有效的)规则集到语句中。
基于费用的方法
考虑由一个SQL语句(连同可得的索引一起)引用的对象的可用统计信息,并
基于这些统计建立一个计划。
优化一个SQL语句的关键是理解Oracle查询优化器如何工作和知道怎样改变Oracle 的行为,以便它能更有效地处理语句。
当然,在优化一个SQL 语句之前,必须知道它在做什么和如何做。今天市场上的许多工具将有助于完成这个工作,而且最有用的工具之一是SQL*Plus中的EXPLAIN PLAN 命令。通过创建一个计划表(通常为PLAN_TABLE)并且检查EXPLAIN PLAN 语句的结果,你会容易地看到Oracle 如何执行一个特定的语句。例如,SQL 语句:
SELECT ename,loc,sal,hiredate
FROM scott.emp, scott.dept
WHERE emp.deptno=dept.deptno;
可用下面的命令解释:
EXPLAIN PLAN SET STATEMENT_ID='DEMO' FOR
SELECT ename,loc,sal,hiredate
FROM scott.emp, scott.dept
WHERE emp.deptno=dept.deptno;
存储在PLAN_TABLE 中的结果可以通过下面的简单查询看到:
SELECT LPAD(' ',2*level) || operation || '' || options || ' '||
object_name EXPLAIN_PLAN
FROM plan_table
CONNECT BY PRIOR id = parent_id
START WITH id=1
看起来像这样:
EXPLAIN_PLAN
NESTED LOOPS
TABLE ACCESSFULL DEPT
TABLE ACCESSFULL EMP
这个计划表明将使用全表扫描来对DEPT 和EMP 表访问。这对像EMP 和DEPT 一样的小表很好,事实上,我们想要它们全表扫描,因为表将缓存到内存中,并且不需要磁盘输入/ 输出(至少在第一次运行之后)。然而,如果表很大,这个查询将进行很长时间,所以我们想改变查询执行的方式。
有3 个基本方法可以改变Oracle 查询优化器的行为:
● 在执行查询时提供一个或多个索引来用
● 重写SQL 来使用一个更为有效的方法
● 以提示(hint)的形式提供查询优化器指导
如果我们试第一选项而且在EMP(deptno)上增加一个索引,计划将改变为:
EXPLAIN_PLAN
NESTED LOOPS
TABLE ACCESSFULL DEPT
TABLE ACCESSBY ROWID EMP
INDEXRANGE SCAN EMPDEPT_IX
现在你能看见Oracle 使用索引通过ROWID 来从EMP 取回行,ROWID 是从新创建的索引中获得的,而不再需要全表扫描。通常使用SQL 会有不止一种实现一个特定功能的方法,在确定使用正确的SQL 语句之前尝试几种不同的方法(有适当的基准)是一种很好的实践方式。第八章“查询最优化”提供了关于SQL 优化的更详细信息。
其他有用的优化特性
Oracle 通过增加提高性能的新特性来不断改良数据库产品。检查即使只有很小更新的Oracle 版本注释是很重要的,因为这其中可能就包含了新的性能特性。你可能觉得有用的一些特性和工具列表如下:
分区表(partitioned table)
从Oracle8 开始分区表就允许在多个子表上创建表,每个子表包含了表数据的一个特定子集。例如,一个表可以按年分区,所有1998 年的数据在一个分区中,所有1999 年的数据在另一个分区中等等。分区对大的表特别有用,因为对包含在一个可识别的子集中的数据进行查询可以在对应的分区中操作,而不用访问其他的分区。例如,更新 1999 年的记录只要求Oracle 在1999 年的分区上执行输入/ 输出操作。可在CREATE TABLE 语句中指定分区。为了使用这个特性,你必须:
● 标识将要定义分区的数据字段(例如sales_year)
● 在CREATE TABLE ... PARTITION BY RANGE 子句中指定值的范围
● 为表的每个分区指定一个另外的表空间(为了得到最好的性能,把表的每个分区放置在不同的磁盘上)。注意单独的表空间不是必需的,但是这种做法可以允许表的一个分区脱机,而维持访问表的剩余部分分区表通常应该伴随着相应的分区索引,如下:
● 使用CREATE INDEX 命令的LOCAL 关键字来告诉Oracle 为索引表的每个分区创建一个单独的索引
● 使用CREATE INDEX 命令的GLOBAL 关键字,以便告诉Oracle 使用不会对应到索引表分区的值来创建一个单一索引。全局(GLOBAL)索引也可以分区
惟一索引表(index-only table)
在某些情况下,正常时存储在一个表中的所有数据可以存放在一个索引中,这样表就没必要了。从Oracle8 开始,一个惟一索引表就使数据按照主键列排序。对这种类型的对象有一些限制:
● 由于数据没有存储在一个表中,所以没有 ROWID 可用
● 必须为表定义一个主键
● 不能创建其他的索引,只有主键可以被创建为索引
惟一索引表通过使用CREATE TABLE命令的ORGANIZATION INDEX子句创建。
位图索引(bitmap index)
当被索引的数据有低基数(cardinality)时(也就是说索引列有相对较少的确定值时),创建位图索引可以大大改善性能。位图索引的一个好的例子就是性别(GENDER),它只有两个值“M”或“F”。对于销售总额(SALES_AMOUNT),将
不适合建立位图索引,因为它对于每行都可能有一个不同的值。
创建一个位图索引类似于创建一个标准的索引,你可以在CREATE INDEX 语句中包括关键字BITMAP。例如,在EMPLOYEE_MASTER 表的GENDER 列创建一个位图索引,指定下列语句:
CREATE BITMAP INDEX empmast_ix ON employee_master(gender);
临时表空间
Oracle7 引进了临时表空间的概念,这专门为Oracle 的排序段使用。通过消除连续不断地分配和释放排序空间的空间管理操作,当排序很大以致内存不能容纳时,所有使用排序的操作将得到性能的提高。这在运行OPS 的时候尤为重要。
注意: 一个临时表空间只能用于排序段,不要在临时表空间中创建永久对象。
要创建一个临时表空间,需要在CREATE TABLESPACE 语句中使用关键字TEMPORARY。例如,下面语句将创建一个叫做TEMP 的临时表空间:
CREATE TABLESPACE TEMP
DATAFILE '/disk99/oracle/oradata/TEST/temp01.dbf' SIZE 50M
DEFAULT STORAGE(INITIAL 64K NEXT 64K MAXEXTENTS UNLIMITED)
TEMPORARY;
一个已存在的非临时表空间可以转变为临时表空间,如果它不包含永久对象,可使用下面SQL 语句进行转换:
ALTER TABLESPACE tablespace TEMPORARY;
不能恢复的操作
由Oracle 7.2 开始,在创建表或索引时就可能不写重做日志记录。这个选项提供了较好的性能,因为需要的输入/ 输出少了很多。要利用这个特性在对象创建语句中指定UNRECOVERABLE(Oracle7语法)或 NOLOGGING(Oracle8 语法)。例如,你想使用数据库链接从另外一个数据库移动数据可使用下面这个语句:
INSERT INTO newtable
SELECT * from oldtable@oldlink;
这个方法当然会奏效,但是将为每次插入创建重做日志记录,这将浪费资源。可以用下面语句完成相同的任务:
CREATE TABLE newtable AS
SELECT * from oldtable@oldlink
NOLOGGING;
当重建索引的时候,NOLOGGING 选项将特别有用。NOLOGGING 关键字可以大大减少索引创建的时间。SQL 语句与下面语句相似:
CREATE INDEX indexname ON table(column)
NOLOGGING;
注意,如果在执行一个不能恢复的语句之后,在某点发生了系统失效,你将不能使用回滚机制来恢复这个事务,你必须意识到一个系统失效已经发生并且要重新运行语句。