《第四章》提高数据库性能
-
想知道性能恶化的原因吗?这篇文章将教会你如何活用OB的功能来提高开发效率,优化数据库性能。我们将分
三回来详细介绍。
■第一回:调整方法
一个运行良好的数据库,常常面临着数据量的增加而性能逐步恶化的困扰,性能问题通常被认为是由于磁
盘容量或者内存等的规格引起的,但实际上是由于SQL处理,也就是“实行计划”没有被正确编写引起的。
实行计划就是执行SQL时,数据库内部是怎样处理SQL的。例如,是否使用索引,多张表存在的时候使用什
么顺序结合等。起初编写的实行计划并不一定是最好的。例如附加的索引没有被使用到。记录的行数很少
时,使用全面搜索更快一些,但如果使用索引,性能的问题就产生了。
那么,怎么才能正确的做成实行计划呢?虽然有SQL的修正和追加索引等手段,但在这之前最重要的是“取
得最新的统计信息”。统计信息是记录各表的记录行数和数据分布的信息。由于实行计划是基于统计信息
决定的,随着数据的增加,性能下降,取得记录数等最新的统计信息,使实行计划最正确,提高性能。
使用SI Object Browser更新统计信息参照以下方法。
1.对象列表中,在表显示的状态下按“Ctrl+A”,选中所有表。(也可以选中处理较慢的SQL所参照的表。)
2.表被选中的状态下点击鼠标右键,选择“统计信息”。
3.统计信息画面显示出来,选中“修正正确的信息”,点击“OK”。
现在只支持Oracle版本,“在对象列表中表示详细信息”可以使详细信息在对象列表中显示。
选择“工具”菜单-->“选项”选项卡。
“对象列表”-->“表示对象固有的详细信息”设置成ON。
“表示”菜单-->“表示更新”。
这个设置有使表的记录数一览表示的优势。
统计信息的更新并不是对应用进行修正,更新统计信息之后还不能解决问题的话,建议使用SQL修正等其
他手段。关于其他的方法,下回介绍。
■第二回:SQL最优化
接上一回,继续进行“如何优化性能”。上回说性能恶化的原因多数是因为SQL,这次要说明怎样使SQL最
优化。
SQL最优化就是“使内存得到最有效的利用”。
在数据库中有叫“程序库缓存”和“数据库缓冲”的内存区域,这些内存主要用于缓存。
把数据的读取作为例子,由于数据存储在硬盘的数据文件中,想要读取数据必须要有权限访问硬盘。但是,
读取硬盘上的文件需要时间,在数据库访问硬盘的时候,内存也在拷贝同样的数据,在第二次之后,只从
内存就可以读取数据,提高了处理速度。这种结构就叫做缓存。
但是,第二次以后并不一定能使用缓存。由于内存大小是有限制的,当容量过大时则不能登录内存。或者
缓存的数据将按照由旧到新的顺序被删除。
也就是说性能恶化的原因多数是由于数据增加,导致从访问内存转换到访问硬盘。活用内存缓存,使处理
速度恢复到从前,这就是性能优化。
具体方法有增加内存,改变初始化参数等,也可以修改SQL文改善缓存。以下介绍两种方法。
使用提示句
不使用索引读取数据时(全表搜索),如果需要很大的内存导致不能访问缓存中的数据,缓存的功能就无
效了。
上回说到,“取得最新的统计信息”方法可以使实行计划得到改善,如果在SQL文中追加以下的提示句也
许会更好。
SELECT /*+ INDEX (表名索引名)*/列名 FROM 表名
在SELECT的后面加上“/*+ INDEX (表名索引名)*/”。这样就可以明确的命令数据库“要使用索引”。
SI Object Browser中使用“实行计划”或者是“索引顾问”功能可以查询索引是否被使用,或者性能有
多大的改善。
使用绑定变量
执行SQL时,数据库内部为了作成SQL实行计划必须要进行SQL解析,这样就会消耗CPU资源。
通常,SQL如果和过去实行过的SQL一样的话,通过程序库缓存就可以直接使用以前的解析结果。
例如,「SELECT * FROM SCOTT.EMP WHERE EMPNO = 1」和「SELECT * FROM SCOTT.EMP WHERE EMPNO = 2」
是不同的SQL,则不能使用缓存功能。
不把SELECT语句的WHERE条件和INSERT语句插入的各项目的值设置成固定值,而是像
「SELECT * FROM SCOTT.EMP WHERE EMPNO = :EMPNO」这样把值的部分设置成变量。这个变量就是绑定变
量。使用绑定变量,因为SQL问变成了一样的,所以可以使用缓存功能。
绑定变量还可以从存储过程,应用发行SQL。以下的代码是以C#为例子,也支持除此之外的主要的程序语
言。
//像Order表插入数据
SqlCommand cm = new SqlCommand(sql, cn);
stringsql = "INSERT INTO ORDER(";
sql += "OrderNo,";
sql += "CustomerNo,";
sql += "Price)";
sql += "VALUES ";
sql += "(@OrderNo,";
sql += "@CustomerNo,";
sql += "@Price)";
cm.CommandText = sql;
//设置各绑定变量的值
cm.Parameters.Add(new SqlParameter("OrderNo", lblOrderNo.Text));
cm.Parameters.Add(new SqlParameter("CustomerNo", lblCustomerNo.ToString()));
cm.Parameters.Add(new SqlParameter("ShippingCharge", lblPrice.Text));
//实行SQL
cm.ExecuteNonQuery();
即使是这样还不能改善性能时,要考虑减轻访问硬盘的负荷,增加内存等方法。首先要最优化比较容易的
SQL。
在最终回,会说明关于其他的性能问题产生的原因以及对策。
■第三回:让原因具体化
上回说性能恶化的原因多数是因为SQL。接下来将要说明如果修正SQL还不能解决问题的话,使用其他的解
决办法。
在Oracle中,发生等待时间原因的事件名和等待时间存在于字典视图中,调查下面的字典视图可以得到原
因。
・V$SYSTEM_EVENT…显示数据库启动后发生的待机事件
・V$SESSION_EVENT…显示现行的session中全部的待机事件
・V$SESSION_WAIT…显示session正在待机中还是待机完了之后的事件
例如想要通过V$SYSTEM_EVENT调查时,使用以下的SQL文。
========================================================
SELECT EVENT, WAIT_CLASS, TOTAL_WAITS, AVERAGE_WAIT
FROM V$SYSTEM_EVENT
WHERE WAIT_CLASS ! = 'Idle'
========================================================
WAIT_CLASS列是'Idle'事件时,也就是空闲时间(等待用户请求的时间),不能作为性能恶化的原因,
所以在WHERE中要除去'Idle'。
实行上述SQL后,EVENT列中表示事件名,TOTAL_WAITS列中表示总待机时间,AVERAGE_WAIT列中表示平均
待机时间,这之中时间长的事件名就是性能恶化的原因。
下记是具有代表性的待机事件和解决方法。
1. db file scattered read/db file sequential read
通过全表搜索,索引搜索读取时发生。
【对策】改善SQL实行计划。
2. direct path write/direct path write temp
磁盘排序处理时花费的等待时间。
【对策】内存有效排序。
3.在“Library”中开始待机事件(log buffer space等)
与程序库缓冲关联的待机事件。
在载入工程的时间和多进程等同时访问时发生。
【对策】减少程序库缓存错误
4.在“log buffer”中开始待机事件(log buffer space等)
与日志文件关联的待机事件。Check point和写入慢时发生。
【对策】增加日志文件大小,增加日志文件组。
各种事件的详细内容请参照一下网址:
http://otndnld.oracle.co.jp/document/products/oracle11g/111/doc_dvd/server.111/E05771-04/waitevents.htm#12032
在V$SYSTEM_EVENT视图中记录了数据库从启动开始到现在的待机事件,有可能不能确定最近的性能恶化的
原因。这时,使用V$SESSION_EVENT视图,比较现象发生前和发生后的差别,就能得到确切的原因。性能
恶化刚刚发生的话,也可以使用V$SESSION_WAIT。
以上三回就是性能优化相关内容。性能优化看起来比较难,但是掌握了关键点的话谁都能操作。这次讲座
如果能对大家有用的话,我们将非常荣幸。