Oracle性能话题涉及面非常广,市场上有很多书籍专门介绍Oracle调优。对性能的追求是无止境的,需要长期不懈的努力;但要避免性能成为问题却不难,甚至可以说很简单。本文从简单实用的角度出发,给出几个提高Oracle处理大量数据效率的有效途径。
一、配置数据库初始化参数
数据库的全部初始化参数可以在OEM中看到。参见下图:
在新建一数据库时,如果不配置这些初始化参数,Oracle会给这些参数以默认值。当数据库规模不大时,采用Oracle的默认值通常不会遇到性能问题。下面介绍对处理大量数据效率有“举足轻重”影响,并且,默认值会带来性能问题的几个参数:
1) db_block_size
该参数设置了Oracle进行一次I/O的基本单位——数据库块的大小 (以字节计)。毫不夸张的说,该参数对于大数据量处理是最重要的一个参数。该参数值设置的越大,对大数据量处理越有利。受操作系统所限,NT4最大只能设置为8K,Win2k最大只能设置为16K。Oracle本身允许的最大值是64K。忘了说了,db_block_size应设置为2的幂。
对于分析型数据库,设置为32K是不错的选择。对于OLTP系统,笔者没有多少经验,网上的说法是设置8K是个比较好的平衡点。Oracle是有许多“神话”的,8K的说法未必符合现在的软硬件情况,更未必符合我们企业的实际情况。如果有时间有机会,比较一下8K好还是16K好总是不会错的。
db_block_size是最基本的一个参数,也是最容易被忽视的一个参数。该参数只能在创建数据库时设置,此后不能更改;一旦有所失误,只能通过重建数据库的方法补救。因此,您建库时应当慎重考虑该参数。
2)
db_file_multiblock_read_count
Oracle官方的说明:在涉及一个完全连续扫描的一次 I/O 操作过程中读取的块的最大数量。对于大的查询来说,进行全表扫描往往比使用索引效率高很多。全表扫描操作是典型的“完全连续扫描”。如果db_block_size设置为32K,db_file_multiblock_read_count设置为8;则一次I/O操作最多可以连续读8个数据库块,即256K。
db_file_multiblock_read_count并非越大越好。对于数据分析系统,db_file_multiblock_read_count和db_block_size的乘积为256K足够了。对于建立在Unix上的OLTP系统,根据网上的说法,二者的乘积为64K是不错的选择。
根据笔者的经验,让数据连续分布在物理磁盘上比考量该参数更加有效。
3) sort_area_size
sort_area_size的重要性可以说是和db_block_size并列的。该参数指出数据库执行一个查询时最多可以使用多大内存来排序。受系统资源所限,我们无法将该参数设置太大。特别是当我们采用独立模式建库时,每个Session都可能会申请一个或多个排序空间。如果我们设置sort_area_size为8M,同时登上来100个用户并发查询,则可能会占去800M内存甚至更多。当主存不够用时,就要用虚拟内存了。如果Oracle被迫使用虚拟内存,则数据库的性能将急剧下降。
对于该参数的设置,网上有人建议至少应超过用于排序记录数的平方根。也就是说,对100万条记录进行排序,每条记录占用1K空间,则sort_area_size至少应设置为1M。对1000万条记录进行排序,每条记录占用1K空间,sort_area_size设置为4M应该够用了。
根据上述数字,OLTP系统的sort_area_size不妨设置为1M或2M;数据分析系统的sort_area_size不妨设置为4M或8M。
db_file_multiblock_read_count和sort_area_size在数据库建立好以后是可以修改的。修改方法很简单。搜索Oracle的安装目录,找到PFILE文件夹(可能会找到多个,其父目录的名字会给我们提示),里面有一个init文本文件,照着里面的内容修改就可以了(找不到相关参数就自己加一个)。修改完毕后重启数据库方能生效。Oracle9i以上版本可以做到不用重启数据库,本文就不介绍了。
二、编写高效的SQL
一般说来,看起来简单的SQL通常都不会遇到性能问题。SQL的执行效率通常比程序的执行效率要高。因此,尽量用SQL解决问题和尽量用简单的SQL解决问题应当是我们开发的指导原则。
编写高效的SQL需要一定的基本功。本文不讨论SQL的理论基础。本文仅介绍一个有用的技术:人为干预SQL的执行计划。当SQL较复杂时,执行计划的可能性会非常多。用最短的时间选择一个最优的执行计划是Oracle奋斗的目标。Oracle数据库有相关的参数来调整挑选执行计划的算法,这些参数本文不讨论,有兴趣的读者可以自己上网去搜。
所谓“人为干预SQL的执行计划”实际上是提示Oracle如何去挑选最优的执行计划。SQL提示的语法很简单:用“/*+”和“*/”将提示包括起来,中间写上关键字就可以了。SQL:提示的关键字有很多,下面介绍几个典型的关键字,更多的用法可以自己上网搜。
1)指定全表扫描:
SELECT /*+FULL(table_name)*/ field1,field2
FROM table_name;
一个大查询如果用到了一个大表中相当一部分的数据,则采用全表扫描的执行计划会比采用索引的执行计划效率高很多。
2)数据直接插入到表的最后,可以提高速度:
INSERT /*+append*/
INTO table_name
select * from table_name1;
Oracle中很多数据块因为曾经做过delete操作而有空闲空间,如果使用append关键字,则Oracle不会去寻找这些有空闲空间的数据块,从而提高了insert语句的执行速度。需注意append关键字只适合于大数据量插入。
三、分区
分区技术相对前面介绍的技术而言要复杂一些。分区实际上是据根据某(些)个字段在物理上将一个大表的数据分开存储,从而,能提高我们查询的效率,同时也能加强我们对数据的管理。典型例子的是根据日期字段分区,从而,当我们查询某个时期的数据时,只需要扫描某个分区的数据而不需要扫描整表的数据。
当我们决定采用分区技术时,只需要在create table语句以及create index语句中增加一些语法。一般的面向DBA的书籍都会有专门的章节介绍分区技术。这里不再赘述。
一个表是否被分区并不影响我们使用:对普通表的操作可以用在分区表上。相反,分区表增加了我们使用该表的灵活性,创建分区表后,我们可以使用Alter table命令来增加、删除、交换、移动、修改、重命名、划分、截断一个已存在分区的结构。一个典型的例子,如果用月份分区,我们可以使用truncate命令在瞬间删除某个月份的全部数据。
我们需要注意的是在创建局部唯一索引时,索引字段应包括分区字段,否则会创建失败。创建全局索引则没有这样的限制。