在复杂应用环境下监控 ORACLE 数据库性能(1)(2)
include "
how_long(TV_START, $timestr);
$conn = OCILogon("username", "password", "dblink");
$stmt = OCIParse($conn, "select ID from users where name='slimzhao'");
OCIDefineByName($stmt, ID, $id);
OCIExecute($stmt);
OCIFetch($stmt);
OCIFreeStatement($stmt);
OCILogoff($conn);
how_long(TV_END, $timestr);
echo " 用户 ID: $id,该操作消耗时间 :$timestr
";
?>
其中 how_long 函数的 PHP 版本如下 :
# 作者 : slimzhao@21cn.com
# 当前维护人 : slimzhao@21cn.com
# 创建日期 : 2001.12.04 00:18:00
# 目的,在一个操作之前或之后调用该函数的不同版本,将得到一个记载了该操作
# 耗费时间的字符串,该函数本身的开销不计入其中。
define("TV_START", 0);
define("TV_END", 1);
function how_long($operation, &$str)
# 返回值 : 0-- 成功,-1-- 传递了非法的参数。
{
global $before_SQL, $after_SQL;
if($operation == TV_START) {
$before_SQL = gettimeofday();
return 0;
} else if($operation == TV_END) {
$after_SQL = gettimeofday();
if($before_SQL["usec"] > $after_SQL["usec"]) {
$str = ($after_SQL["sec"] - $before_SQL["sec"] - 1)." 秒 ".
($after_SQL["usec"] + 1000*1000 -$before_SQL["usec"])." 微秒 ";
} else {
$str = ($after_SQL["sec"] - $before_SQL["sec"])." 秒 ".
($after_SQL["usec"]-$before_SQL["usec"])." 微秒 ";
}
} else {
return -1;
}
}
?>
上面的数据库操作开销的计算仅限于对时间消耗的计算,对同时使用同一数据库的其它应用软件的影响,对磁盘操作的频繁程度,数据库操作所采取的具体策略等等因素,都未考虑在内,高级语言也不可能提供这样的参考数据。 而数据库本身提供的监测手段弥补了这一不足。 最简单的操作控制台 :sqlplus SQL> set timing on
将为每次执行的数据库操作进行计时,精度为 1/100 秒,笔者对该功能的使用中发现其时间的计算也有一定的偏差。 而且时间偏差很大,严格说来,已不属于误差的范围,该归错误了,下面是一个例子中得到的数据 :
[bash$] cat tmp.sql
set timing on
host date;
select count(*) from users;
host date;
SQL> @tmp.sql
Wed Dec 5 00:21:01 CST 2001
COUNT(*)
----------
1243807
Elapsed: 00:00:06.16
Wed Dec 5 00:21:05 CST 2001
从系统的时间差来看,为 4 秒左右,但 ORACLE 却报告了 6.16 秒 !
如果说 ORACLE 工具在时间计算上太差强人意的话,在 SQL 语句的执行方案上可算是对 SQL 语句如何执行的最权威的诠释了。 解读这样的信息需要对 ORACLE 内部对 SQL 操作的过程有一定了解,下面是该功能的一样典型示例 :
SQL> set autotrace on
SQL> select count(*) from users;
COUNT(*)
----------
1243807
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=1)
1 0 SORT (AGGREGATE)
2 1 INDEX (FAST FULL SCAN) OF 'USER_BASEINFO$NAME' (UNIQUE)
(Cost=4 Card=1244840)
Statistics
----------------------------------------------------------
0 recursive calls
4 db block gets
3032 consistent gets
3033 physical reads
0 redo size
370 bytes sent via SQL*Net to client
424 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
Execution Plan 下的信息显示 ORACLE 制定了一个什么样的计划来完成 SQL 操作的 ,SQL 语言是一种 4GL 语言,其特点是告诉系统做什么,而不提供如何做的信息。 当然,最终的具体工作总得有人做的,只是由数据库自动制定而不是程序员人为指定一个具体的操作步骤,制作这个步骤当然要有所依据,ORACLE 有两个基本原则来决定如何优化 : cost-based( 基于开销的优化 ) 和 rule-based( 基于规则的优化 ). 基于开销的优化的工作方式依赖于数据库对 SQL 语句所操作的数据对象 ( 可简单认为就是表 ) 的数据特征的统计特性进行收集和分析。 收集分析的工作由 DBA 来定期执行,时间间隔依数据变化频率而定,以保持统计数据一定的准确性,具体操作请参照 analyze 语句。 Oracle 准备在将来的版本中取消对基于开销的优化方案的支持,因为这种方案需要大量的数据收集与分析工作,且总会有一定的误差,这造成最终的执行方案往往不是最优的。
基于规则的优化则是依据一些数据操作效率的规则进行选择,优化的核心在于效率,时间上尽可能短,空间上尽可能少进行 IO 操作。 两种优化方案都绝非十全十美,ORACLE 虽将其称为优化方案,笔者的观察结果表明,ORACLE 制定出一个不是最优或错误的执行方案也是完全可能的。 以上为例,Oracle 的优化策略是 Choose, 所谓 Choose 就是 cost-based 或 rule-based,让 ORACLE 自己选择,可以通过数据库启动初始化文件 initXXX.ora 文件中的 optimizer_mode 参数来指定。
言归正传,上面的具体策略是 Oracle 对该表的一个唯一索引进行全扫描,因为在数据库里一个字段如果可以建立一个 UNIQUE 类型的索引,那么它就与表中的记录有一一对应的关系。 所以对该索引进行 count(*) 可以保证其值等于对表进行 count(*) 操作。 对索引进行全扫描后的上层操作是一个集合操作,即对找到的每个索引记录进行计数。 对这些信息的观察主要用来确定 ORACLE 是否选用了 SQL 程序员希望 ORACLE 选用的索引操作。
Statistics 给出了执行该 SQL 操作所消耗的资源的统计数据,信息的表达一目了然,所有这些值都是越小越好,以通过 SQL*Net 的数据吞吐量为例,在 OCI 编程中使用以下技术可显著减少网络流量 : 通过将 Commit 操作与 Execute 操作绑定为一个操作。 通过对数组进行成批数据的 delete, insert, update, 通过对一个 SELECT 语句指定一个预取记录数。 这些统计数据中,尤其需要避免的是涉及磁盘存取的操作,因为多级存储的操作速度是 CPU >> Memory >> HD > Disc > network > disk



